1 // Written in the D programming language. 2 3 /** 4 A one-stop shop for converting values from one type to another. 5 6 $(SCRIPT inhibitQuickIndex = 1;) 7 $(DIVC quickindex, 8 $(BOOKTABLE, 9 $(TR $(TH Category) $(TH Functions)) 10 $(TR $(TD Generic) $(TD 11 $(LREF asOriginalType) 12 $(LREF castFrom) 13 $(LREF parse) 14 $(LREF to) 15 $(LREF toChars) 16 $(LREF bitCast) 17 )) 18 $(TR $(TD Strings) $(TD 19 $(LREF text) 20 $(LREF wtext) 21 $(LREF dtext) 22 $(LREF writeText) 23 $(LREF writeWText) 24 $(LREF writeDText) 25 $(LREF hexString) 26 )) 27 $(TR $(TD Numeric) $(TD 28 $(LREF octal) 29 $(LREF roundTo) 30 $(LREF signed) 31 $(LREF unsigned) 32 )) 33 $(TR $(TD Exceptions) $(TD 34 $(LREF ConvException) 35 $(LREF ConvOverflowException) 36 )) 37 )) 38 39 Copyright: Copyright The D Language Foundation 2007-. 40 41 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 42 43 Authors: $(HTTP digitalmars.com, Walter Bright), 44 $(HTTP erdani.org, Andrei Alexandrescu), 45 Shin Fujishiro, 46 Adam D. Ruppe, 47 Kenji Hara 48 49 Source: $(PHOBOSSRC std/conv.d) 50 51 */ 52 module std.conv; 53 54 public import std.ascii : LetterCase; 55 56 import std.meta; 57 import std.range; 58 import std.traits; 59 import std.typecons : Flag, Yes, No, tuple, isTuple; 60 61 // Same as std.string.format, but "self-importing". 62 // Helps reduce code and imports, particularly in static asserts. 63 // Also helps with missing imports errors. 64 package template convFormat() 65 { 66 import std.format : format; 67 alias convFormat = format; 68 } 69 70 /* ************* Exceptions *************** */ 71 72 /** 73 * Thrown on conversion errors. 74 */ 75 class ConvException : Exception 76 { 77 import std.exception : basicExceptionCtors; 78 /// 79 mixin basicExceptionCtors; 80 } 81 82 /// 83 @safe unittest 84 { 85 import std.exception : assertThrown; 86 assertThrown!ConvException(to!int("abc")); 87 } 88 89 private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__) 90 { 91 string msg; 92 93 if (source.empty) 94 msg = "Unexpected end of input when converting from type " ~ S.stringof ~ " to type " ~ T.stringof; 95 else 96 { 97 ElementType!S el = source.front; 98 99 if (el == '\n') 100 msg = text("Unexpected '\\n' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); 101 else 102 msg = text("Unexpected '", el, 103 "' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); 104 } 105 106 return new ConvException(msg, fn, ln); 107 } 108 109 @safe pure/* nothrow*/ // lazy parameter bug 110 private auto parseError(lazy string msg, string fn = __FILE__, size_t ln = __LINE__) 111 { 112 return new ConvException(text("Can't parse string: ", msg), fn, ln); 113 } 114 115 private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = __LINE__) 116 { 117 if (source.empty) 118 throw parseError(text("unexpected end of input when expecting \"", c, "\"")); 119 if (source.front != c) 120 throw parseError(text("\"", c, "\" is missing"), fn, ln); 121 source.popFront(); 122 } 123 124 private 125 { 126 T toStr(T, S)(S src) 127 if (isSomeString!T) 128 { 129 // workaround for https://issues.dlang.org/show_bug.cgi?id=14198 130 static if (is(S == bool) && is(typeof({ T s = "string"; }))) 131 { 132 return src ? "true" : "false"; 133 } 134 else 135 { 136 import std.array : appender; 137 import std.format.spec : FormatSpec; 138 import std.format.write : formatValue; 139 140 auto w = appender!T(); 141 FormatSpec!(ElementEncodingType!T) f; 142 formatValue(w, src, f); 143 return w.data; 144 } 145 } 146 147 template isExactSomeString(T) 148 { 149 enum isExactSomeString = isSomeString!T && !is(T == enum); 150 } 151 152 template isEnumStrToStr(S, T) 153 { 154 enum isEnumStrToStr = is(S : T) && 155 is(S == enum) && isExactSomeString!T; 156 } 157 template isNullToStr(S, T) 158 { 159 enum isNullToStr = is(S : T) && 160 (is(immutable S == immutable typeof(null))) && isExactSomeString!T; 161 } 162 } 163 164 /** 165 * Thrown on conversion overflow errors. 166 */ 167 class ConvOverflowException : ConvException 168 { 169 @safe pure nothrow 170 this(string s, string fn = __FILE__, size_t ln = __LINE__) 171 { 172 super(s, fn, ln); 173 } 174 } 175 176 /// 177 @safe unittest 178 { 179 import std.exception : assertThrown; 180 assertThrown!ConvOverflowException(to!ubyte(1_000_000)); 181 } 182 183 /** 184 The `to` template converts a value from one type _to another. 185 The source type is deduced and the target type must be specified, for example the 186 expression `to!int(42.0)` converts the number 42 from 187 `double` _to `int`. The conversion is "safe", i.e., 188 it checks for overflow; `to!int(4.2e10)` would throw the 189 `ConvOverflowException` exception. Overflow checks are only 190 inserted when necessary, e.g., `to!double(42)` does not do 191 any checking because any `int` fits in a `double`. 192 193 Conversions from string _to numeric types differ from the C equivalents 194 `atoi()` and `atol()` by checking for overflow and not allowing whitespace. 195 196 For conversion of strings _to signed types, the grammar recognized is: 197 $(PRE $(I Integer): 198 $(I Sign UnsignedInteger) 199 $(I UnsignedInteger) 200 $(I Sign): 201 $(B +) 202 $(B -)) 203 204 For conversion _to unsigned types, the grammar recognized is: 205 $(PRE $(I UnsignedInteger): 206 $(I DecimalDigit) 207 $(I DecimalDigit) $(I UnsignedInteger)) 208 */ 209 template to(T) 210 { 211 T to(A...)(A args) 212 if (A.length > 0) 213 { 214 return toImpl!T(args); 215 } 216 217 // Fix https://issues.dlang.org/show_bug.cgi?id=6175 218 T to(S)(ref S arg) 219 if (isStaticArray!S) 220 { 221 return toImpl!T(arg); 222 } 223 224 // Fix https://issues.dlang.org/show_bug.cgi?id=16108 225 T to(S)(ref S arg) 226 if (isAggregateType!S && !isCopyable!S) 227 { 228 return toImpl!T(arg); 229 } 230 } 231 232 /** 233 * Converting a value _to its own type (useful mostly for generic code) 234 * simply returns its argument. 235 */ 236 @safe pure unittest 237 { 238 int a = 42; 239 int b = to!int(a); 240 double c = to!double(3.14); // c is double with value 3.14 241 } 242 243 /** 244 * Converting among numeric types is a safe way _to cast them around. 245 * 246 * Conversions from floating-point types _to integral types allow loss of 247 * precision (the fractional part of a floating-point number). The 248 * conversion is truncating towards zero, the same way a cast would 249 * truncate. (_To round a floating point value when casting _to an 250 * integral, use `roundTo`.) 251 */ 252 @safe pure unittest 253 { 254 import std.exception : assertThrown; 255 256 int a = 420; 257 assert(to!long(a) == a); 258 assertThrown!ConvOverflowException(to!byte(a)); 259 260 assert(to!int(4.2e6) == 4200000); 261 assertThrown!ConvOverflowException(to!uint(-3.14)); 262 assert(to!uint(3.14) == 3); 263 assert(to!uint(3.99) == 3); 264 assert(to!int(-3.99) == -3); 265 } 266 267 /** 268 * When converting strings _to numeric types, note that D hexadecimal and binary 269 * literals are not handled. Neither the prefixes that indicate the base, nor the 270 * horizontal bar used _to separate groups of digits are recognized. This also 271 * applies to the suffixes that indicate the type. 272 * 273 * _To work around this, you can specify a radix for conversions involving numbers. 274 */ 275 @safe pure unittest 276 { 277 auto str = to!string(42, 16); 278 assert(str == "2A"); 279 auto i = to!int(str, 16); 280 assert(i == 42); 281 } 282 283 /** 284 * Conversions from integral types _to floating-point types always 285 * succeed, but might lose accuracy. The largest integers with a 286 * predecessor representable in floating-point format are `2^24-1` for 287 * `float`, `2^53-1` for `double`, and `2^64-1` for `real` (when 288 * `real` is 80-bit, e.g. on Intel machines). 289 */ 290 @safe pure unittest 291 { 292 // 2^24 - 1, largest proper integer representable as float 293 int a = 16_777_215; 294 assert(to!int(to!float(a)) == a); 295 assert(to!int(to!float(-a)) == -a); 296 } 297 298 /** 299 Conversion from string types to char types enforces the input 300 to consist of a single code point, and said code point must 301 fit in the target type. Otherwise, $(LREF ConvException) is thrown. 302 */ 303 @safe pure unittest 304 { 305 import std.exception : assertThrown; 306 307 assert(to!char("a") == 'a'); 308 assertThrown(to!char("ñ")); // 'ñ' does not fit into a char 309 assert(to!wchar("ñ") == 'ñ'); 310 assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar 311 assert(to!dchar("😃") == '😃'); 312 313 // Using wstring or dstring as source type does not affect the result 314 assert(to!char("a"w) == 'a'); 315 assert(to!char("a"d) == 'a'); 316 317 // Two code points cannot be converted to a single one 318 assertThrown(to!char("ab")); 319 } 320 321 /** 322 * Converting an array _to another array type works by converting each 323 * element in turn. Associative arrays can be converted _to associative 324 * arrays as long as keys and values can in turn be converted. 325 */ 326 @safe pure unittest 327 { 328 import std.string : split; 329 330 int[] a = [1, 2, 3]; 331 auto b = to!(float[])(a); 332 assert(b == [1.0f, 2, 3]); 333 string str = "1 2 3 4 5 6"; 334 auto numbers = to!(double[])(split(str)); 335 assert(numbers == [1.0, 2, 3, 4, 5, 6]); 336 int[string] c; 337 c["a"] = 1; 338 c["b"] = 2; 339 auto d = to!(double[wstring])(c); 340 assert(d["a"w] == 1 && d["b"w] == 2); 341 } 342 343 /** 344 * Conversions operate transitively, meaning that they work on arrays and 345 * associative arrays of any complexity. 346 * 347 * This conversion works because `to!short` applies _to an `int`, `to!wstring` 348 * applies _to a `string`, `to!string` applies _to a `double`, and 349 * `to!(double[])` applies _to an `int[]`. The conversion might throw an 350 * exception because `to!short` might fail the range check. 351 */ 352 @safe unittest 353 { 354 int[string][double[int[]]] a; 355 auto b = to!(short[wstring][string[double[]]])(a); 356 } 357 358 /** 359 * Object-to-object conversions by dynamic casting throw exception when 360 * the source is non-null and the target is null. 361 */ 362 @safe pure unittest 363 { 364 import std.exception : assertThrown; 365 // Testing object conversions 366 class A {} 367 class B : A {} 368 class C : A {} 369 A a1 = new A, a2 = new B, a3 = new C; 370 assert(to!B(a2) is a2); 371 assert(to!C(a3) is a3); 372 assertThrown!ConvException(to!B(a3)); 373 } 374 375 /** 376 * Stringize conversion from all types is supported. 377 * $(UL 378 * $(LI String _to string conversion works for any two string types having 379 * (`char`, `wchar`, `dchar`) character widths and any 380 * combination of qualifiers (mutable, `const`, or `immutable`).) 381 * $(LI Converts array (other than strings) _to string. 382 * Each element is converted by calling `to!T`.) 383 * $(LI Associative array _to string conversion. 384 * Each element is converted by calling `to!T`.) 385 * $(LI Object _to string conversion calls `toString` against the object or 386 * returns `"null"` if the object is null.) 387 * $(LI Struct _to string conversion calls `toString` against the struct if 388 * it is defined.) 389 * $(LI For structs that do not define `toString`, the conversion _to string 390 * produces the list of fields.) 391 * $(LI Enumerated types are converted _to strings as their symbolic names.) 392 * $(LI Boolean values are converted to `"true"` or `"false"`.) 393 * $(LI `char`, `wchar`, `dchar` _to a string type.) 394 * $(LI Unsigned or signed integers _to strings. 395 * $(DL $(DT [special case]) 396 * $(DD Convert integral value _to string in $(D_PARAM radix) radix. 397 * radix must be a value from 2 to 36. 398 * value is treated as a signed value only if radix is 10. 399 * The characters A through Z are used to represent values 10 through 36 400 * and their case is determined by the $(D_PARAM letterCase) parameter.))) 401 * $(LI All floating point types _to all string types.) 402 * $(LI Pointer to string conversions convert the pointer to a `size_t` value. 403 * If pointer is `char*`, treat it as C-style strings. 404 * In that case, this function is `@system`.)) 405 * See $(REF formatValue, std,format) on how `toString` should be defined. 406 */ 407 @system pure unittest // @system due to cast and ptr 408 { 409 // Conversion representing dynamic/static array with string 410 long[] a = [ 1, 3, 5 ]; 411 assert(to!string(a) == "[1, 3, 5]"); 412 413 // Conversion representing associative array with string 414 int[string] associativeArray = ["0":1, "1":2]; 415 assert(to!string(associativeArray) == `["0":1, "1":2]` || 416 to!string(associativeArray) == `["1":2, "0":1]`); 417 418 // char* to string conversion 419 assert(to!string(cast(char*) null) == ""); 420 assert(to!string("foo\0".ptr) == "foo"); 421 422 // Conversion reinterpreting void array to string 423 auto w = "abcx"w; 424 const(void)[] b = w; 425 assert(b.length == 8); 426 427 auto c = to!(wchar[])(b); 428 assert(c == "abcx"); 429 } 430 431 /** 432 * Strings can be converted to enum types. The enum member with the same name as the 433 * input string is returned. The comparison is case-sensitive. 434 * 435 * A $(LREF ConvException) is thrown if the enum does not have the specified member. 436 */ 437 @safe pure unittest 438 { 439 import std.exception : assertThrown; 440 441 enum E { a, b, c } 442 assert(to!E("a") == E.a); 443 assert(to!E("b") == E.b); 444 assertThrown!ConvException(to!E("A")); 445 } 446 447 // Tests for https://issues.dlang.org/show_bug.cgi?id=6175 448 @safe pure nothrow unittest 449 { 450 char[9] sarr = "blablabla"; 451 auto darr = to!(char[])(sarr); 452 assert(sarr.ptr == darr.ptr); 453 assert(sarr.length == darr.length); 454 } 455 456 // Tests for https://issues.dlang.org/show_bug.cgi?id=7348 457 @safe pure /+nothrow+/ unittest 458 { 459 assert(to!string(null) == "null"); 460 assert(text(null) == "null"); 461 } 462 463 // Test `scope` inference of parameters of `text` 464 @safe unittest 465 { 466 static struct S 467 { 468 int* x; // make S a type with pointers 469 string toString() const scope 470 { 471 return "S"; 472 } 473 } 474 scope S s; 475 assert(text("a", s) == "aS"); 476 } 477 478 // Tests for https://issues.dlang.org/show_bug.cgi?id=11390 479 @safe pure /+nothrow+/ unittest 480 { 481 const(typeof(null)) ctn; 482 immutable(typeof(null)) itn; 483 assert(to!string(ctn) == "null"); 484 assert(to!string(itn) == "null"); 485 } 486 487 // Tests for https://issues.dlang.org/show_bug.cgi?id=8729: do NOT skip leading WS 488 @safe pure unittest 489 { 490 import std.exception; 491 static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 492 { 493 assertThrown!ConvException(to!T(" 0")); 494 assertThrown!ConvException(to!T(" 0", 8)); 495 } 496 static foreach (T; AliasSeq!(float, double, real)) 497 { 498 assertThrown!ConvException(to!T(" 0")); 499 } 500 501 assertThrown!ConvException(to!bool(" true")); 502 503 alias NullType = typeof(null); 504 assertThrown!ConvException(to!NullType(" null")); 505 506 alias ARR = int[]; 507 assertThrown!ConvException(to!ARR(" [1]")); 508 509 alias AA = int[int]; 510 assertThrown!ConvException(to!AA(" [1:1]")); 511 } 512 513 // https://issues.dlang.org/show_bug.cgi?id=20623 514 @safe pure nothrow unittest 515 { 516 // static class C 517 // { 518 // override string toString() const 519 // { 520 // return "C()"; 521 // } 522 // } 523 524 static struct S 525 { 526 bool b; 527 int i; 528 float f; 529 int[] a; 530 int[int] aa; 531 S* p; 532 // C c; // TODO: Fails because of hasToString 533 534 void fun() inout 535 { 536 static foreach (const idx; 0 .. this.tupleof.length) 537 { 538 { 539 const _ = this.tupleof[idx].to!string(); 540 } 541 } 542 } 543 } 544 } 545 546 private T toImpl(T, S)(S) 547 if (isInputRange!S && isInfinite!S && isExactSomeString!T) 548 { 549 static assert(0, "Cannot convert infinite range to string. " ~ 550 "Use `std.range.take` or `std.range.takeExactly` to make it finite."); 551 } 552 553 // Test for issue : https://github.com/dlang/phobos/issues/10559 554 @safe pure nothrow unittest 555 { 556 import std.range : repeat; 557 import std.conv : to; 558 559 // Test that converting an infinite range doesn't compile 560 static assert(!__traits(compiles, repeat(1).to!string)); 561 } 562 563 /** 564 If the source type is implicitly convertible to the target type, $(D 565 to) simply performs the implicit conversion. 566 */ 567 private T toImpl(T, S)(S value) 568 if (is(S : T) && 569 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) 570 { 571 template isSignedInt(T) 572 { 573 enum isSignedInt = isIntegral!T && isSigned!T; 574 } 575 alias isUnsignedInt = isUnsigned; 576 577 // Conversion from integer to integer, and changing its sign 578 static if (isUnsignedInt!S && isSignedInt!T && S.sizeof == T.sizeof) 579 { // unsigned to signed & same size 580 import std.exception : enforce; 581 enforce(value <= cast(S) T.max, 582 new ConvOverflowException("Conversion positive overflow")); 583 } 584 else static if (isSignedInt!S && isUnsignedInt!T) 585 { // signed to unsigned 586 import std.exception : enforce; 587 enforce(0 <= value, 588 new ConvOverflowException("Conversion negative overflow")); 589 } 590 591 return value; 592 } 593 594 // https://issues.dlang.org/show_bug.cgi?id=9523: Allow identity enum conversion 595 @safe pure nothrow unittest 596 { 597 enum E { a } 598 auto e = to!E(E.a); 599 assert(e == E.a); 600 } 601 602 @safe pure nothrow unittest 603 { 604 int a = 42; 605 auto b = to!long(a); 606 assert(a == b); 607 } 608 609 // https://issues.dlang.org/show_bug.cgi?id=6377 610 @safe pure unittest 611 { 612 import std.exception; 613 // Conversion between same size 614 static foreach (S; AliasSeq!(byte, short, int, long)) 615 {{ 616 alias U = Unsigned!S; 617 618 static foreach (Sint; AliasSeq!(S, const S, immutable S)) 619 static foreach (Uint; AliasSeq!(U, const U, immutable U)) 620 {{ 621 // positive overflow 622 Uint un = Uint.max; 623 assertThrown!ConvOverflowException(to!Sint(un), 624 text(Sint.stringof, ' ', Uint.stringof, ' ', un)); 625 626 // negative overflow 627 Sint sn = -1; 628 assertThrown!ConvOverflowException(to!Uint(sn), 629 text(Sint.stringof, ' ', Uint.stringof, ' ', un)); 630 }} 631 }} 632 633 // Conversion between different size 634 static foreach (i, S1; AliasSeq!(byte, short, int, long)) 635 static foreach ( S2; AliasSeq!(byte, short, int, long)[i+1..$]) 636 {{ 637 alias U1 = Unsigned!S1; 638 alias U2 = Unsigned!S2; 639 640 static assert(U1.sizeof < S2.sizeof); 641 642 // small unsigned to big signed 643 static foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) 644 static foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) 645 {{ 646 Uint un = Uint.max; 647 assertNotThrown(to!Sint(un)); 648 assert(to!Sint(un) == un); 649 }} 650 651 // big unsigned to small signed 652 static foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) 653 static foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) 654 {{ 655 Uint un = Uint.max; 656 assertThrown(to!Sint(un)); 657 }} 658 659 static assert(S1.sizeof < U2.sizeof); 660 661 // small signed to big unsigned 662 static foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) 663 static foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) 664 {{ 665 Sint sn = -1; 666 assertThrown!ConvOverflowException(to!Uint(sn)); 667 }} 668 669 // big signed to small unsigned 670 static foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) 671 static foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) 672 {{ 673 Sint sn = -1; 674 assertThrown!ConvOverflowException(to!Uint(sn)); 675 }} 676 }} 677 } 678 679 // https://issues.dlang.org/show_bug.cgi?id=13551 680 private T toImpl(T, S)(S value) 681 if (isTuple!T) 682 { 683 T t; 684 static foreach (i; 0 .. T.length) 685 { 686 t[i] = value[i].to!(typeof(T[i])); 687 } 688 return t; 689 } 690 691 @safe unittest 692 { 693 import std.typecons : Tuple; 694 695 auto test = ["10", "20", "30"]; 696 assert(test.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(10, 20, 30)); 697 698 auto test1 = [1, 2]; 699 assert(test1.to!(Tuple!(int, int)) == Tuple!(int, int)(1, 2)); 700 701 auto test2 = [1.0, 2.0, 3.0]; 702 assert(test2.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(1, 2, 3)); 703 } 704 705 /* 706 Converting static arrays forwards to their dynamic counterparts. 707 */ 708 private T toImpl(T, S)(ref S s) 709 if (isStaticArray!S) 710 { 711 return toImpl!(T, typeof(s[0])[])(s); 712 } 713 714 @safe pure nothrow unittest 715 { 716 char[4] test = ['a', 'b', 'c', 'd']; 717 static assert(!isInputRange!(Unqual!(char[4]))); 718 assert(to!string(test) == test); 719 } 720 721 /** 722 When source type supports member template function opCast, it is used. 723 */ 724 private T toImpl(T, S)(S value) 725 if (!is(S : T) && 726 is(typeof(S.init.opCast!T()) : T) && 727 !isExactSomeString!T && 728 !is(typeof(T(value)))) 729 { 730 return value.opCast!T(); 731 } 732 733 @safe pure unittest 734 { 735 static struct Test 736 { 737 struct T 738 { 739 this(S s) @safe pure { } 740 } 741 struct S 742 { 743 T opCast(U)() @safe pure { assert(false); } 744 } 745 } 746 cast(void) to!(Test.T)(Test.S()); 747 748 // make sure std.conv.to is doing the same thing as initialization 749 Test.S s; 750 Test.T t = s; 751 } 752 753 @safe pure unittest 754 { 755 class B 756 { 757 T opCast(T)() { return 43; } 758 } 759 auto b = new B; 760 assert(to!int(b) == 43); 761 762 struct S 763 { 764 T opCast(T)() { return 43; } 765 } 766 auto s = S(); 767 assert(to!int(s) == 43); 768 } 769 770 /** 771 When target type supports 'converting construction', it is used. 772 $(UL $(LI If target type is struct, `T(value)` is used.) 773 $(LI If target type is class, $(D new T(value)) is used.)) 774 */ 775 private T toImpl(T, S)(S value) 776 if (!is(S : T) && 777 is(T == struct) && is(typeof(T(value)))) 778 { 779 return T(value); 780 } 781 782 // https://issues.dlang.org/show_bug.cgi?id=3961 783 @safe pure unittest 784 { 785 struct Int 786 { 787 int x; 788 } 789 Int i = to!Int(1); 790 791 static struct Int2 792 { 793 int x; 794 this(int x) @safe pure { this.x = x; } 795 } 796 Int2 i2 = to!Int2(1); 797 798 static struct Int3 799 { 800 int x; 801 static Int3 opCall(int x) @safe pure 802 { 803 Int3 i; 804 i.x = x; 805 return i; 806 } 807 } 808 Int3 i3 = to!Int3(1); 809 } 810 811 // https://issues.dlang.org/show_bug.cgi?id=6808 812 @safe pure unittest 813 { 814 static struct FakeBigInt 815 { 816 this(string s) @safe pure {} 817 } 818 819 string s = "101"; 820 auto i3 = to!FakeBigInt(s); 821 } 822 823 /// ditto 824 private T toImpl(T, S)(S value) 825 if (!is(S : T) && 826 is(T == class) && is(typeof(new T(value)))) 827 { 828 return new T(value); 829 } 830 831 @safe pure unittest 832 { 833 static struct S 834 { 835 int x; 836 } 837 static class C 838 { 839 int x; 840 this(int x) @safe pure { this.x = x; } 841 } 842 843 static class B 844 { 845 int value; 846 this(S src) @safe pure { value = src.x; } 847 this(C src) @safe pure { value = src.x; } 848 } 849 850 S s = S(1); 851 auto b1 = to!B(s); // == new B(s) 852 assert(b1.value == 1); 853 854 C c = new C(2); 855 auto b2 = to!B(c); // == new B(c) 856 assert(b2.value == 2); 857 858 auto c2 = to!C(3); // == new C(3) 859 assert(c2.x == 3); 860 } 861 862 @safe pure unittest 863 { 864 struct S 865 { 866 class A 867 { 868 this(B b) @safe pure {} 869 } 870 class B : A 871 { 872 this() @safe pure { super(this); } 873 } 874 } 875 876 S.B b = new S.B(); 877 S.A a = to!(S.A)(b); // == cast(S.A) b 878 // (do not run construction conversion like new S.A(b)) 879 assert(b is a); 880 881 static class C : Object 882 { 883 this() @safe pure {} 884 this(Object o) @safe pure {} 885 } 886 887 Object oc = new C(); 888 C a2 = to!C(oc); // == new C(a) 889 // Construction conversion overrides down-casting conversion 890 assert(a2 !is a); // 891 } 892 893 /** 894 Object-to-object conversions by dynamic casting throw exception when the source is 895 non-null and the target is null. 896 */ 897 private T toImpl(T, S)(S value) 898 if (!is(S : T) && 899 (is(S == class) || is(S == interface)) && !is(typeof(value.opCast!T()) : T) && 900 (is(T == class) || is(T == interface)) && !is(typeof(new T(value)))) 901 { 902 static if (is(T == immutable)) 903 { 904 // immutable <- immutable 905 enum isModConvertible = is(S == immutable); 906 } 907 else static if (is(T == const)) 908 { 909 static if (is(T == shared)) 910 { 911 // shared const <- shared 912 // shared const <- shared const 913 // shared const <- immutable 914 enum isModConvertible = is(S == shared) || is(S == immutable); 915 } 916 else 917 { 918 // const <- mutable 919 // const <- immutable 920 enum isModConvertible = !is(S == shared); 921 } 922 } 923 else 924 { 925 static if (is(T == shared)) 926 { 927 // shared <- shared mutable 928 enum isModConvertible = is(S == shared) && !is(S == const); 929 } 930 else 931 { 932 // (mutable) <- (mutable) 933 enum isModConvertible = is(Unqual!S == S); 934 } 935 } 936 static assert(isModConvertible, "Bad modifier conversion: "~S.stringof~" to "~T.stringof); 937 938 auto result = ()@trusted{ return cast(T) value; }(); 939 if (!result && value) 940 { 941 string name(TypeInfo ti) @trusted 942 { 943 while (auto tc = (cast(TypeInfo_Const) ti)) 944 { 945 ti = tc.base; 946 } 947 if (auto tinf = cast(TypeInfo_Interface) ti) 948 { 949 ti = tinf.info; 950 } 951 TypeInfo_Class tc = cast(TypeInfo_Class) ti; 952 assert(tc); 953 return tc.name; 954 } 955 throw new ConvException("Cannot convert object of static type " ~ 956 name(typeid(S)) ~ " and dynamic type " ~ name(typeid(value)) ~ " to type " ~ name(typeid(T))); 957 } 958 return result; 959 } 960 961 // Unittest for 6288 962 @safe pure unittest 963 { 964 import std.exception; 965 966 alias Identity(T) = T; 967 alias toConst(T) = const T; 968 alias toShared(T) = shared T; 969 alias toSharedConst(T) = shared const T; 970 alias toImmutable(T) = immutable T; 971 template AddModifier(int n) 972 if (0 <= n && n < 5) 973 { 974 static if (n == 0) alias AddModifier = Identity; 975 else static if (n == 1) alias AddModifier = toConst; 976 else static if (n == 2) alias AddModifier = toShared; 977 else static if (n == 3) alias AddModifier = toSharedConst; 978 else static if (n == 4) alias AddModifier = toImmutable; 979 } 980 981 interface I {} 982 interface J {} 983 984 class A {} 985 class B : A {} 986 class C : B, I, J {} 987 class D : I {} 988 989 static foreach (m1; 0 .. 5) // enumerate modifiers 990 static foreach (m2; 0 .. 5) // ditto 991 {{ 992 alias srcmod = AddModifier!m1; 993 alias tgtmod = AddModifier!m2; 994 995 // Compile time convertible equals to modifier convertible. 996 static if (is(srcmod!Object : tgtmod!Object)) 997 { 998 // Test runtime conversions: class to class, class to interface, 999 // interface to class, and interface to interface 1000 1001 // Check that the runtime conversion to succeed 1002 srcmod!A ac = new srcmod!C(); 1003 srcmod!I ic = new srcmod!C(); 1004 assert(to!(tgtmod!C)(ac) !is null); // A(c) to C 1005 assert(to!(tgtmod!I)(ac) !is null); // A(c) to I 1006 assert(to!(tgtmod!C)(ic) !is null); // I(c) to C 1007 assert(to!(tgtmod!J)(ic) !is null); // I(c) to J 1008 1009 // Check that the runtime conversion fails 1010 srcmod!A ab = new srcmod!B(); 1011 srcmod!I id = new srcmod!D(); 1012 assertThrown(to!(tgtmod!C)(ab)); // A(b) to C 1013 assertThrown(to!(tgtmod!I)(ab)); // A(b) to I 1014 assertThrown(to!(tgtmod!C)(id)); // I(d) to C 1015 assertThrown(to!(tgtmod!J)(id)); // I(d) to J 1016 } 1017 else 1018 { 1019 // Check that the conversion is rejected statically 1020 static assert(!is(typeof(to!(tgtmod!C)(srcmod!A.init)))); // A to C 1021 static assert(!is(typeof(to!(tgtmod!I)(srcmod!A.init)))); // A to I 1022 static assert(!is(typeof(to!(tgtmod!C)(srcmod!I.init)))); // I to C 1023 static assert(!is(typeof(to!(tgtmod!J)(srcmod!I.init)))); // I to J 1024 } 1025 }} 1026 } 1027 1028 /** 1029 Handles type _to string conversions 1030 */ 1031 private T toImpl(T, S)(S value) 1032 if (!(is(S : T) && 1033 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && 1034 !isInfinite!S && isExactSomeString!T) 1035 { 1036 static if (isExactSomeString!S && value[0].sizeof == ElementEncodingType!T.sizeof) 1037 { 1038 // string-to-string with incompatible qualifier conversion 1039 static if (is(ElementEncodingType!T == immutable)) 1040 { 1041 // conversion (mutable|const) -> immutable 1042 return value.idup; 1043 } 1044 else 1045 { 1046 // conversion (immutable|const) -> mutable 1047 return value.dup; 1048 } 1049 } 1050 else static if (isExactSomeString!S) 1051 { 1052 import std.array : appender; 1053 // other string-to-string 1054 //Use Appender directly instead of toStr, which also uses a formatedWrite 1055 auto w = appender!T(); 1056 w.put(value); 1057 return w.data; 1058 } 1059 else static if (isIntegral!S && !is(S == enum)) 1060 { 1061 // other integral-to-string conversions with default radix 1062 1063 import core.internal.string : signedToTempString, unsignedToTempString; 1064 1065 alias EEType = Unqual!(ElementEncodingType!T); 1066 EEType[long.sizeof * 3 + 1] buf = void; 1067 EEType[] t = isSigned!S 1068 ? signedToTempString!(10, false, EEType)(value, buf) 1069 : unsignedToTempString!(10, false, EEType)(value, buf); 1070 return t.dup; 1071 } 1072 else static if (is(S == void[]) || is(S == const(void)[]) || is(S == immutable(void)[])) 1073 { 1074 import core.stdc.string : memcpy; 1075 import std.exception : enforce; 1076 // Converting void array to string 1077 alias Char = Unqual!(ElementEncodingType!T); 1078 auto raw = cast(const(ubyte)[]) value; 1079 enforce(raw.length % Char.sizeof == 0, 1080 new ConvException("Alignment mismatch in converting a " 1081 ~ S.stringof ~ " to a " 1082 ~ T.stringof)); 1083 auto result = new Char[raw.length / Char.sizeof]; 1084 ()@trusted{ memcpy(result.ptr, value.ptr, value.length); }(); 1085 return cast(T) result; 1086 } 1087 else static if (isPointer!S && isSomeChar!(PointerTarget!S)) 1088 { 1089 // This is unsafe because we cannot guarantee that the pointer is null terminated. 1090 return () @system { 1091 static if (is(S : const(char)*)) 1092 import core.stdc.string : strlen; 1093 else 1094 size_t strlen(S s) nothrow 1095 { 1096 S p = s; 1097 while (*p++) {} 1098 return p-s-1; 1099 } 1100 return toImpl!T(value ? value[0 .. strlen(value)].dup : null); 1101 }(); 1102 } 1103 else static if (isSomeString!T && is(S == enum)) 1104 { 1105 static if (isSwitchable!(OriginalType!S) && EnumMembers!S.length <= 50) 1106 { 1107 switch (value) 1108 { 1109 foreach (member; NoDuplicates!(EnumMembers!S)) 1110 { 1111 case member: 1112 return to!T(enumRep!(immutable(T), S, member)); 1113 } 1114 default: 1115 } 1116 } 1117 else 1118 { 1119 foreach (member; EnumMembers!S) 1120 { 1121 if (value == member) 1122 return to!T(enumRep!(immutable(T), S, member)); 1123 } 1124 } 1125 1126 import std.array : appender; 1127 import std.format.spec : FormatSpec; 1128 import std.format.write : formatValue; 1129 1130 //Default case, delegate to format 1131 //Note: we don't call toStr directly, to avoid duplicate work. 1132 auto app = appender!T(); 1133 app.put("cast(" ~ S.stringof ~ ")"); 1134 FormatSpec!char f; 1135 formatValue(app, cast(OriginalType!S) value, f); 1136 return app.data; 1137 } 1138 else 1139 { 1140 // other non-string values runs formatting 1141 return toStr!T(value); 1142 } 1143 } 1144 1145 // https://issues.dlang.org/show_bug.cgi?id=14042 1146 @system unittest 1147 { 1148 immutable(char)* ptr = "hello".ptr; 1149 auto result = ptr.to!(char[]); 1150 } 1151 // https://issues.dlang.org/show_bug.cgi?id=8384 1152 @system unittest 1153 { 1154 void test1(T)(T lp, string cmp) 1155 { 1156 static foreach (e; AliasSeq!(char, wchar, dchar)) 1157 { 1158 test2!(e[])(lp, cmp); 1159 test2!(const(e)[])(lp, cmp); 1160 test2!(immutable(e)[])(lp, cmp); 1161 } 1162 } 1163 1164 void test2(D, S)(S lp, string cmp) 1165 { 1166 assert(to!string(to!D(lp)) == cmp); 1167 } 1168 1169 static foreach (e; AliasSeq!("Hello, world!", "Hello, world!"w, "Hello, world!"d)) 1170 { 1171 test1(e, "Hello, world!"); 1172 test1(e.ptr, "Hello, world!"); 1173 } 1174 static foreach (e; AliasSeq!("", ""w, ""d)) 1175 { 1176 test1(e, ""); 1177 test1(e.ptr, ""); 1178 } 1179 } 1180 1181 /* 1182 To string conversion for non copy-able structs 1183 */ 1184 private T toImpl(T, S)(ref S value) 1185 if (!(is(S : T) && 1186 !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && 1187 !isInfinite!S && isExactSomeString!T && !isCopyable!S && !isStaticArray!S) 1188 { 1189 import std.array : appender; 1190 import std.format.spec : FormatSpec; 1191 import std.format.write : formatValue; 1192 1193 auto w = appender!T(); 1194 FormatSpec!(ElementEncodingType!T) f; 1195 formatValue(w, value, f); 1196 return w.data; 1197 } 1198 1199 // https://issues.dlang.org/show_bug.cgi?id=16108 1200 @safe unittest 1201 { 1202 static struct A 1203 { 1204 int val; 1205 bool flag; 1206 1207 string toString() { return text(val, ":", flag); } 1208 1209 @disable this(this); 1210 } 1211 1212 auto a = A(); 1213 assert(to!string(a) == "0:false"); 1214 1215 static struct B 1216 { 1217 int val; 1218 bool flag; 1219 1220 @disable this(this); 1221 } 1222 1223 auto b = B(); 1224 assert(to!string(b) == "B(0, false)"); 1225 } 1226 1227 // https://issues.dlang.org/show_bug.cgi?id=20070 1228 @safe unittest 1229 { 1230 void writeThem(T)(ref inout(T) them) 1231 { 1232 assert(them.to!string == "[1, 2, 3, 4]"); 1233 } 1234 1235 const(uint)[4] vals = [ 1, 2, 3, 4 ]; 1236 writeThem(vals); 1237 } 1238 1239 /* 1240 Check whether type `T` can be used in a switch statement. 1241 This is useful for compile-time generation of switch case statements. 1242 */ 1243 private template isSwitchable(E) 1244 { 1245 enum bool isSwitchable = is(typeof({ 1246 switch (E.init) { default: } 1247 })); 1248 } 1249 1250 // 1251 @safe unittest 1252 { 1253 static assert(isSwitchable!int); 1254 static assert(!isSwitchable!double); 1255 static assert(!isSwitchable!real); 1256 } 1257 1258 //Static representation of the index I of the enum S, 1259 //In representation T. 1260 //T must be an immutable string (avoids un-necessary initializations). 1261 private template enumRep(T, S, S value) 1262 if (is (T == immutable) && isExactSomeString!T && is(S == enum)) 1263 { 1264 static T enumRep = toStr!T(value); 1265 } 1266 1267 @safe pure unittest 1268 { 1269 import std.exception; 1270 void dg() 1271 { 1272 // string to string conversion 1273 alias Chars = AliasSeq!(char, wchar, dchar); 1274 foreach (LhsC; Chars) 1275 { 1276 alias LhStrings = AliasSeq!(LhsC[], const(LhsC)[], immutable(LhsC)[]); 1277 foreach (Lhs; LhStrings) 1278 { 1279 foreach (RhsC; Chars) 1280 { 1281 alias RhStrings = AliasSeq!(RhsC[], const(RhsC)[], immutable(RhsC)[]); 1282 foreach (Rhs; RhStrings) 1283 { 1284 Lhs s1 = to!Lhs("wyda"); 1285 Rhs s2 = to!Rhs(s1); 1286 //writeln(Lhs.stringof, " -> ", Rhs.stringof); 1287 assert(s1 == to!Lhs(s2)); 1288 } 1289 } 1290 } 1291 } 1292 1293 foreach (T; Chars) 1294 { 1295 foreach (U; Chars) 1296 { 1297 T[] s1 = to!(T[])("Hello, world!"); 1298 auto s2 = to!(U[])(s1); 1299 assert(s1 == to!(T[])(s2)); 1300 auto s3 = to!(const(U)[])(s1); 1301 assert(s1 == to!(T[])(s3)); 1302 auto s4 = to!(immutable(U)[])(s1); 1303 assert(s1 == to!(T[])(s4)); 1304 } 1305 } 1306 } 1307 dg(); 1308 assertCTFEable!dg; 1309 } 1310 1311 @safe pure unittest 1312 { 1313 // Conversion representing bool value with string 1314 bool b; 1315 assert(to!string(b) == "false"); 1316 b = true; 1317 assert(to!string(b) == "true"); 1318 } 1319 1320 @safe pure unittest 1321 { 1322 // Conversion representing character value with string 1323 alias AllChars = 1324 AliasSeq!( char, const( char), immutable( char), 1325 wchar, const(wchar), immutable(wchar), 1326 dchar, const(dchar), immutable(dchar)); 1327 foreach (Char1; AllChars) 1328 { 1329 foreach (Char2; AllChars) 1330 { 1331 Char1 c = 'a'; 1332 assert(to!(Char2[])(c)[0] == c); 1333 } 1334 uint x = 4; 1335 assert(to!(Char1[])(x) == "4"); 1336 } 1337 1338 string s = "foo"; 1339 string s2; 1340 foreach (char c; s) 1341 { 1342 s2 ~= to!string(c); 1343 } 1344 assert(s2 == "foo"); 1345 } 1346 1347 @safe pure nothrow unittest 1348 { 1349 import std.exception; 1350 // Conversion representing integer values with string 1351 1352 static foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong)) 1353 { 1354 assert(to!string(Int(0)) == "0"); 1355 assert(to!string(Int(9)) == "9"); 1356 assert(to!string(Int(123)) == "123"); 1357 } 1358 1359 static foreach (Int; AliasSeq!(byte, short, int, long)) 1360 { 1361 assert(to!string(Int(0)) == "0"); 1362 assert(to!string(Int(9)) == "9"); 1363 assert(to!string(Int(123)) == "123"); 1364 assert(to!string(Int(-0)) == "0"); 1365 assert(to!string(Int(-9)) == "-9"); 1366 assert(to!string(Int(-123)) == "-123"); 1367 assert(to!string(const(Int)(6)) == "6"); 1368 } 1369 1370 assert(wtext(int.max) == "2147483647"w); 1371 assert(wtext(int.min) == "-2147483648"w); 1372 assert(to!string(0L) == "0"); 1373 1374 assertCTFEable!( 1375 { 1376 assert(to!string(1uL << 62) == "4611686018427387904"); 1377 assert(to!string(0x100000000) == "4294967296"); 1378 assert(to!string(-138L) == "-138"); 1379 }); 1380 } 1381 1382 @safe unittest // sprintf issue 1383 { 1384 double[2] a = [ 1.5, 2.5 ]; 1385 assert(to!string(a) == "[1.5, 2.5]"); 1386 } 1387 1388 @safe unittest 1389 { 1390 // Conversion representing class object with string 1391 class A 1392 { 1393 override string toString() @safe const { return "an A"; } 1394 } 1395 A a; 1396 assert(to!string(a) == "null"); 1397 a = new A; 1398 assert(to!string(a) == "an A"); 1399 1400 // https://issues.dlang.org/show_bug.cgi?id=7660 1401 class C { override string toString() @safe const { return "C"; } } 1402 struct S { C c; alias c this; } 1403 S s; s.c = new C(); 1404 assert(to!string(s) == "C"); 1405 } 1406 1407 @safe unittest 1408 { 1409 // Conversion representing struct object with string 1410 struct S1 1411 { 1412 string toString() { return "wyda"; } 1413 } 1414 assert(to!string(S1()) == "wyda"); 1415 1416 struct S2 1417 { 1418 int a = 42; 1419 float b = 43.5; 1420 } 1421 S2 s2; 1422 assert(to!string(s2) == "S2(42, 43.5)"); 1423 1424 // Test for https://issues.dlang.org/show_bug.cgi?id=8080 1425 struct S8080 1426 { 1427 short[4] data; 1428 alias data this; 1429 string toString() { return "<S>"; } 1430 } 1431 S8080 s8080; 1432 assert(to!string(s8080) == "<S>"); 1433 } 1434 1435 @safe unittest 1436 { 1437 // Conversion representing enum value with string 1438 enum EB : bool { a = true } 1439 enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned 1440 // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909) 1441 enum EI : int { a = -1, b = 0, c = 1 } 1442 enum EF : real { a = 1.414, b = 1.732, c = 2.236 } 1443 enum EC : char { a = 'x', b = 'y' } 1444 enum ES : string { a = "aaa", b = "bbb" } 1445 1446 static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) 1447 { 1448 assert(to! string(E.a) == "a"c); 1449 assert(to!wstring(E.a) == "a"w); 1450 assert(to!dstring(E.a) == "a"d); 1451 } 1452 1453 // Test an value not corresponding to an enum member. 1454 auto o = cast(EU) 5; 1455 assert(to! string(o) == "cast(EU)5"c); 1456 assert(to!wstring(o) == "cast(EU)5"w); 1457 assert(to!dstring(o) == "cast(EU)5"d); 1458 } 1459 1460 @safe unittest 1461 { 1462 enum E 1463 { 1464 foo, 1465 doo = foo, // check duplicate switch statements 1466 bar, 1467 } 1468 1469 //Test regression 12494 1470 assert(to!string(E.foo) == "foo"); 1471 assert(to!string(E.doo) == "foo"); 1472 assert(to!string(E.bar) == "bar"); 1473 1474 static foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[]))) 1475 {{ 1476 auto s1 = to!S(E.foo); 1477 auto s2 = to!S(E.foo); 1478 assert(s1 == s2); 1479 // ensure we don't allocate when it's unnecessary 1480 assert(s1 is s2); 1481 }} 1482 1483 static foreach (S; AliasSeq!(char[], wchar[], dchar[])) 1484 {{ 1485 auto s1 = to!S(E.foo); 1486 auto s2 = to!S(E.foo); 1487 assert(s1 == s2); 1488 // ensure each mutable array is unique 1489 assert(s1 !is s2); 1490 }} 1491 } 1492 1493 // ditto 1494 @trusted pure private T toImpl(T, S)(S value, uint radix, LetterCase letterCase = LetterCase.upper) 1495 if (isIntegral!S && 1496 isExactSomeString!T) 1497 in 1498 { 1499 assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]"); 1500 } 1501 do 1502 { 1503 alias EEType = Unqual!(ElementEncodingType!T); 1504 1505 T toStringRadixConvert(size_t bufLen)(uint runtimeRadix = 0) 1506 { 1507 Unsigned!(Unqual!S) div = void, mValue = unsigned(value); 1508 1509 size_t index = bufLen; 1510 EEType[bufLen] buffer = void; 1511 char baseChar = letterCase == LetterCase.lower ? 'a' : 'A'; 1512 char mod = void; 1513 1514 do 1515 { 1516 div = cast(S)(mValue / runtimeRadix ); 1517 mod = cast(ubyte)(mValue % runtimeRadix); 1518 mod += mod < 10 ? '0' : baseChar - 10; 1519 buffer[--index] = cast(char) mod; 1520 mValue = div; 1521 } while (mValue); 1522 1523 return cast(T) buffer[index .. $].dup; 1524 } 1525 1526 import std.array : array; 1527 switch (radix) 1528 { 1529 case 10: 1530 // The (value+0) is so integral promotions happen to the type 1531 return toChars!(10, EEType)(value + 0).array; 1532 case 16: 1533 // The unsigned(unsigned(value)+0) is so unsigned integral promotions happen to the type 1534 if (letterCase == letterCase.upper) 1535 return toChars!(16, EEType, LetterCase.upper)(unsigned(unsigned(value) + 0)).array; 1536 else 1537 return toChars!(16, EEType, LetterCase.lower)(unsigned(unsigned(value) + 0)).array; 1538 case 2: 1539 return toChars!(2, EEType)(unsigned(unsigned(value) + 0)).array; 1540 case 8: 1541 return toChars!(8, EEType)(unsigned(unsigned(value) + 0)).array; 1542 1543 default: 1544 return toStringRadixConvert!(S.sizeof * 6)(radix); 1545 } 1546 } 1547 1548 @safe pure nothrow unittest 1549 { 1550 static foreach (Int; AliasSeq!(uint, ulong)) 1551 { 1552 assert(to!string(Int(16), 16) == "10"); 1553 assert(to!string(Int(15), 2u) == "1111"); 1554 assert(to!string(Int(1), 2u) == "1"); 1555 assert(to!string(Int(0x1234AF), 16u) == "1234AF"); 1556 assert(to!string(Int(0x1234BCD), 16u, LetterCase.upper) == "1234BCD"); 1557 assert(to!string(Int(0x1234AF), 16u, LetterCase.lower) == "1234af"); 1558 } 1559 1560 static foreach (Int; AliasSeq!(int, long)) 1561 { 1562 assert(to!string(Int(-10), 10u) == "-10"); 1563 } 1564 1565 assert(to!string(byte(-10), 16) == "F6"); 1566 assert(to!string(long.min) == "-9223372036854775808"); 1567 assert(to!string(long.max) == "9223372036854775807"); 1568 } 1569 1570 /** 1571 Narrowing numeric-numeric conversions throw when the value does not 1572 fit in the narrower type. 1573 */ 1574 private T toImpl(T, S)(S value) 1575 if (!is(S : T) && 1576 (isNumeric!S || isSomeChar!S || isBoolean!S) && 1577 (isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum)) 1578 { 1579 static if (isFloatingPoint!S && isIntegral!T) 1580 { 1581 import std.math.traits : isNaN; 1582 if (value.isNaN) throw new ConvException("Input was NaN"); 1583 } 1584 1585 enum sSmallest = mostNegative!S; 1586 enum tSmallest = mostNegative!T; 1587 static if (sSmallest < 0) 1588 { 1589 // possible underflow converting from a signed 1590 static if (tSmallest == 0) 1591 { 1592 immutable good = value >= 0; 1593 } 1594 else 1595 { 1596 static assert(tSmallest < 0, 1597 "minimum value of T must be smaller than 0"); 1598 immutable good = value >= tSmallest; 1599 } 1600 if (!good) 1601 throw new ConvOverflowException("Conversion negative overflow"); 1602 } 1603 static if (S.max > T.max) 1604 { 1605 // possible overflow 1606 if (value > T.max) 1607 throw new ConvOverflowException("Conversion positive overflow"); 1608 } 1609 return (ref value)@trusted{ return cast(T) value; }(value); 1610 } 1611 1612 @safe pure unittest 1613 { 1614 import std.exception; 1615 1616 dchar a = ' '; 1617 assert(to!char(a) == ' '); 1618 a = 300; 1619 assert(collectException(to!char(a))); 1620 1621 dchar from0 = 'A'; 1622 char to0 = to!char(from0); 1623 1624 wchar from1 = 'A'; 1625 char to1 = to!char(from1); 1626 1627 char from2 = 'A'; 1628 char to2 = to!char(from2); 1629 1630 char from3 = 'A'; 1631 wchar to3 = to!wchar(from3); 1632 1633 char from4 = 'A'; 1634 dchar to4 = to!dchar(from4); 1635 } 1636 1637 @safe unittest 1638 { 1639 import std.exception; 1640 1641 // Narrowing conversions from enum -> integral should be allowed, but they 1642 // should throw at runtime if the enum value doesn't fit in the target 1643 // type. 1644 enum E1 : ulong { A = 1, B = 1UL << 48, C = 0 } 1645 assert(to!int(E1.A) == 1); 1646 assert(to!bool(E1.A) == true); 1647 assertThrown!ConvOverflowException(to!int(E1.B)); // E1.B overflows int 1648 assertThrown!ConvOverflowException(to!bool(E1.B)); // E1.B overflows bool 1649 assert(to!bool(E1.C) == false); 1650 1651 enum E2 : long { A = -1L << 48, B = -1 << 31, C = 1 << 31 } 1652 assertThrown!ConvOverflowException(to!int(E2.A)); // E2.A overflows int 1653 assertThrown!ConvOverflowException(to!uint(E2.B)); // E2.B overflows uint 1654 assert(to!int(E2.B) == -1 << 31); // but does not overflow int 1655 assert(to!int(E2.C) == 1 << 31); // E2.C does not overflow int 1656 1657 enum E3 : int { A = -1, B = 1, C = 255, D = 0 } 1658 assertThrown!ConvOverflowException(to!ubyte(E3.A)); 1659 assertThrown!ConvOverflowException(to!bool(E3.A)); 1660 assert(to!byte(E3.A) == -1); 1661 assert(to!byte(E3.B) == 1); 1662 assert(to!ubyte(E3.C) == 255); 1663 assert(to!bool(E3.B) == true); 1664 assertThrown!ConvOverflowException(to!byte(E3.C)); 1665 assertThrown!ConvOverflowException(to!bool(E3.C)); 1666 assert(to!bool(E3.D) == false); 1667 1668 } 1669 1670 @safe unittest 1671 { 1672 import std.exception; 1673 import std.math.traits : isNaN; 1674 1675 double d = double.nan; 1676 float f = to!float(d); 1677 assert(f.isNaN); 1678 assert(to!double(f).isNaN); 1679 assertThrown!ConvException(to!int(d)); 1680 assertThrown!ConvException(to!int(f)); 1681 auto ex = collectException(d.to!int); 1682 assert(ex.msg == "Input was NaN"); 1683 } 1684 1685 /** 1686 Array-to-array conversion (except when target is a string type) 1687 converts each element in turn by using `to`. 1688 */ 1689 private T toImpl(T, S)(scope S value) 1690 if (!is(S : T) && 1691 !isSomeString!S && isDynamicArray!S && 1692 !isExactSomeString!T && isArray!T) 1693 { 1694 alias E = typeof(T.init[0]); 1695 1696 static if (isStaticArray!T) 1697 { 1698 import std.exception : enforce; 1699 auto res = to!(E[])(value); 1700 enforce!ConvException(T.length == res.length, 1701 convFormat("Length mismatch when converting to static array: %s vs %s", T.length, res.length)); 1702 return res[0 .. T.length]; 1703 } 1704 else 1705 { 1706 import std.array : appender; 1707 auto w = appender!(E[])(); 1708 w.reserve(value.length); 1709 foreach (ref e; value) 1710 { 1711 w.put(to!E(e)); 1712 } 1713 return w.data; 1714 } 1715 } 1716 1717 @safe pure unittest 1718 { 1719 import std.exception; 1720 1721 // array to array conversions 1722 uint[] a = [ 1u, 2, 3 ]; 1723 auto b = to!(float[])(a); 1724 assert(b == [ 1.0f, 2, 3 ]); 1725 1726 immutable(int)[3] d = [ 1, 2, 3 ]; 1727 b = to!(float[])(d); 1728 assert(b == [ 1.0f, 2, 3 ]); 1729 1730 uint[][] e = [ a, a ]; 1731 auto f = to!(float[][])(e); 1732 assert(f[0] == b && f[1] == b); 1733 1734 // Test for https://issues.dlang.org/show_bug.cgi?id=8264 1735 struct Wrap 1736 { 1737 string wrap; 1738 alias wrap this; 1739 } 1740 Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work 1741 1742 // https://issues.dlang.org/show_bug.cgi?id=12633 1743 import std.conv : to; 1744 const s2 = ["10", "20"]; 1745 1746 immutable int[2] a3 = s2.to!(int[2]); 1747 assert(a3 == [10, 20]); 1748 1749 // verify length mismatches are caught 1750 immutable s4 = [1, 2, 3, 4]; 1751 foreach (i; [1, 4]) 1752 { 1753 auto ex = collectException(s4[0 .. i].to!(int[2])); 1754 assert(ex && ex.msg == "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i + '0')], 1755 ex ? ex.msg : "Exception was not thrown!"); 1756 } 1757 } 1758 1759 @safe unittest 1760 { 1761 auto b = [ 1.0f, 2, 3 ]; 1762 1763 auto c = to!(string[])(b); 1764 assert(c[0] == "1" && c[1] == "2" && c[2] == "3"); 1765 } 1766 1767 /** 1768 Associative array to associative array conversion converts each key 1769 and each value in turn. 1770 */ 1771 private T toImpl(T, S)(S value) 1772 if (!is(S : T) && isAssociativeArray!S && 1773 isAssociativeArray!T && !is(T == enum)) 1774 { 1775 /* This code is potentially unsafe. 1776 */ 1777 alias K2 = KeyType!T; 1778 alias V2 = ValueType!T; 1779 1780 // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end 1781 Unqual!V2[K2] result; 1782 1783 foreach (k1, v1; value) 1784 { 1785 // Cast values temporarily to Unqual!V2 to store them to result variable 1786 result[to!K2(k1)] = to!(Unqual!V2)(v1); 1787 } 1788 // Cast back to original type 1789 return () @trusted { return cast(T) result; }(); 1790 } 1791 1792 @safe unittest 1793 { 1794 // hash to hash conversions 1795 int[string] a; 1796 a["0"] = 1; 1797 a["1"] = 2; 1798 auto b = to!(double[dstring])(a); 1799 assert(b["0"d] == 1 && b["1"d] == 2); 1800 } 1801 1802 // https://issues.dlang.org/show_bug.cgi?id=8705, from doc 1803 @safe unittest 1804 { 1805 import std.exception; 1806 int[string][double[int[]]] a; 1807 auto b = to!(short[wstring][string[double[]]])(a); 1808 a = [null:["hello":int.max]]; 1809 assertThrown!ConvOverflowException(to!(short[wstring][string[double[]]])(a)); 1810 } 1811 @system unittest // Extra cases for AA with qualifiers conversion 1812 { 1813 int[][int[]] a;// = [[], []]; 1814 auto b = to!(immutable(short[])[immutable short[]])(a); 1815 1816 double[dstring][int[long[]]] c; 1817 auto d = to!(immutable(short[immutable wstring])[immutable string[double[]]])(c); 1818 } 1819 1820 @safe unittest 1821 { 1822 import std.algorithm.comparison : equal; 1823 import std.array : byPair; 1824 1825 int[int] a; 1826 assert(a.to!(int[int]) == a); 1827 assert(a.to!(const(int)[int]).byPair.equal(a.byPair)); 1828 } 1829 1830 @safe pure unittest 1831 { 1832 static void testIntegralToFloating(Integral, Floating)() 1833 { 1834 Integral a = 42; 1835 auto b = to!Floating(a); 1836 assert(a == b); 1837 assert(a == to!Integral(b)); 1838 } 1839 static void testFloatingToIntegral(Floating, Integral)() 1840 { 1841 import std.math.traits : floatTraits, RealFormat; 1842 1843 bool convFails(Source, Target, E)(Source src) 1844 { 1845 try 1846 cast(void) to!Target(src); 1847 catch (E) 1848 return true; 1849 return false; 1850 } 1851 1852 // convert some value 1853 Floating a = 4.2e1; 1854 auto b = to!Integral(a); 1855 assert(is(typeof(b) == Integral) && b == 42); 1856 // convert some negative value (if applicable) 1857 a = -4.2e1; 1858 static if (Integral.min < 0) 1859 { 1860 b = to!Integral(a); 1861 assert(is(typeof(b) == Integral) && b == -42); 1862 } 1863 else 1864 { 1865 // no go for unsigned types 1866 assert(convFails!(Floating, Integral, ConvOverflowException)(a)); 1867 } 1868 // convert to the smallest integral value 1869 a = 0.0 + Integral.min; 1870 static if (Integral.min < 0) 1871 { 1872 a = -a; // -Integral.min not representable as an Integral 1873 assert(convFails!(Floating, Integral, ConvOverflowException)(a) 1874 || Floating.sizeof <= Integral.sizeof 1875 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1876 } 1877 a = 0.0 + Integral.min; 1878 assert(to!Integral(a) == Integral.min); 1879 --a; // no more representable as an Integral 1880 assert(convFails!(Floating, Integral, ConvOverflowException)(a) 1881 || Floating.sizeof <= Integral.sizeof 1882 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1883 a = 0.0 + Integral.max; 1884 assert(to!Integral(a) == Integral.max 1885 || Floating.sizeof <= Integral.sizeof 1886 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1887 ++a; // no more representable as an Integral 1888 assert(convFails!(Floating, Integral, ConvOverflowException)(a) 1889 || Floating.sizeof <= Integral.sizeof 1890 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53); 1891 // convert a value with a fractional part 1892 a = 3.14; 1893 assert(to!Integral(a) == 3); 1894 a = 3.99; 1895 assert(to!Integral(a) == 3); 1896 static if (Integral.min < 0) 1897 { 1898 a = -3.14; 1899 assert(to!Integral(a) == -3); 1900 a = -3.99; 1901 assert(to!Integral(a) == -3); 1902 } 1903 } 1904 1905 alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); 1906 alias AllFloats = AliasSeq!(float, double, real); 1907 alias AllNumerics = AliasSeq!(AllInts, AllFloats); 1908 // test with same type 1909 { 1910 foreach (T; AllNumerics) 1911 { 1912 T a = 42; 1913 auto b = to!T(a); 1914 assert(is(typeof(a) == typeof(b)) && a == b); 1915 } 1916 } 1917 // test that floating-point numbers convert properly to largest ints 1918 // see http://oregonstate.edu/~peterseb/mth351/docs/351s2001_fp80x87.html 1919 // look for "largest fp integer with a predecessor" 1920 { 1921 // float 1922 int a = 16_777_215; // 2^24 - 1 1923 assert(to!int(to!float(a)) == a); 1924 assert(to!int(to!float(-a)) == -a); 1925 // double 1926 long b = 9_007_199_254_740_991; // 2^53 - 1 1927 assert(to!long(to!double(b)) == b); 1928 assert(to!long(to!double(-b)) == -b); 1929 // real 1930 static if (real.mant_dig >= 64) 1931 { 1932 ulong c = 18_446_744_073_709_551_615UL; // 2^64 - 1 1933 assert(to!ulong(to!real(c)) == c); 1934 } 1935 } 1936 // test conversions floating => integral 1937 { 1938 foreach (Integral; AllInts) 1939 { 1940 foreach (Floating; AllFloats) 1941 { 1942 testFloatingToIntegral!(Floating, Integral)(); 1943 } 1944 } 1945 } 1946 // test conversion integral => floating 1947 { 1948 foreach (Integral; AllInts) 1949 { 1950 foreach (Floating; AllFloats) 1951 { 1952 testIntegralToFloating!(Integral, Floating)(); 1953 } 1954 } 1955 } 1956 // test parsing 1957 { 1958 foreach (T; AllNumerics) 1959 { 1960 // from type immutable(char)[2] 1961 auto a = to!T("42"); 1962 assert(a == 42); 1963 // from type char[] 1964 char[] s1 = "42".dup; 1965 a = to!T(s1); 1966 assert(a == 42); 1967 // from type char[2] 1968 char[2] s2; 1969 s2[] = "42"; 1970 a = to!T(s2); 1971 assert(a == 42); 1972 // from type immutable(wchar)[2] 1973 a = to!T("42"w); 1974 assert(a == 42); 1975 } 1976 } 1977 } 1978 1979 @safe unittest 1980 { 1981 alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); 1982 alias AllFloats = AliasSeq!(float, double, real); 1983 alias AllNumerics = AliasSeq!(AllInts, AllFloats); 1984 // test conversions to string 1985 { 1986 foreach (T; AllNumerics) 1987 { 1988 T a = 42; 1989 string s = to!string(a); 1990 assert(s == "42", s); 1991 wstring ws = to!wstring(a); 1992 assert(ws == "42"w, to!string(ws)); 1993 dstring ds = to!dstring(a); 1994 assert(ds == "42"d, to!string(ds)); 1995 // array test 1996 T[] b = new T[2]; 1997 b[0] = 42; 1998 b[1] = 33; 1999 assert(to!string(b) == "[42, 33]"); 2000 } 2001 } 2002 // test array to string conversion 2003 foreach (T ; AllNumerics) 2004 { 2005 auto a = [to!T(1), 2, 3]; 2006 assert(to!string(a) == "[1, 2, 3]"); 2007 } 2008 // test enum to int conversion 2009 enum Testing { Test1, Test2 } 2010 Testing t; 2011 auto a = to!string(t); 2012 assert(a == "Test1"); 2013 } 2014 2015 2016 /** 2017 String, or string-like input range, to non-string conversion runs parsing. 2018 $(UL 2019 $(LI When the source is a wide string, it is first converted to a narrow 2020 string and then parsed.) 2021 $(LI When the source is a narrow string, normal text parsing occurs.)) 2022 */ 2023 private T toImpl(T, S)(S value) 2024 if (isInputRange!S && isSomeChar!(ElementEncodingType!S) && 2025 !isExactSomeString!T && is(typeof(parse!T(value))) && 2026 // https://issues.dlang.org/show_bug.cgi?id=20539 2027 !(is(T == enum) && is(typeof(value == OriginalType!T.init)) && !isSomeString!(OriginalType!T))) 2028 { 2029 scope(success) 2030 { 2031 if (!value.empty) 2032 { 2033 throw convError!(S, T)(value); 2034 } 2035 } 2036 return parse!T(value); 2037 } 2038 2039 /// ditto 2040 private T toImpl(T, S)(S value, uint radix) 2041 if (isSomeFiniteCharInputRange!S && 2042 isIntegral!T && is(typeof(parse!T(value, radix)))) 2043 { 2044 scope(success) 2045 { 2046 if (!value.empty) 2047 { 2048 throw convError!(S, T)(value); 2049 } 2050 } 2051 return parse!T(value, radix); 2052 } 2053 2054 @safe pure unittest 2055 { 2056 // https://issues.dlang.org/show_bug.cgi?id=6668 2057 // ensure no collaterals thrown 2058 try { to!uint("-1"); } 2059 catch (ConvException e) { assert(e.next is null); } 2060 } 2061 2062 @safe pure unittest 2063 { 2064 static foreach (Str; AliasSeq!(string, wstring, dstring)) 2065 {{ 2066 Str a = "123"; 2067 assert(to!int(a) == 123); 2068 assert(to!double(a) == 123); 2069 }} 2070 2071 // https://issues.dlang.org/show_bug.cgi?id=6255 2072 auto n = to!int("FF", 16); 2073 assert(n == 255); 2074 } 2075 2076 // https://issues.dlang.org/show_bug.cgi?id=15800 2077 @safe unittest 2078 { 2079 import std.utf : byCodeUnit, byChar, byWchar, byDchar; 2080 2081 assert(to!int(byCodeUnit("10")) == 10); 2082 assert(to!int(byCodeUnit("10"), 10) == 10); 2083 assert(to!int(byCodeUnit("10"w)) == 10); 2084 assert(to!int(byCodeUnit("10"w), 10) == 10); 2085 2086 assert(to!int(byChar("10")) == 10); 2087 assert(to!int(byChar("10"), 10) == 10); 2088 assert(to!int(byWchar("10")) == 10); 2089 assert(to!int(byWchar("10"), 10) == 10); 2090 assert(to!int(byDchar("10")) == 10); 2091 assert(to!int(byDchar("10"), 10) == 10); 2092 } 2093 2094 /** 2095 String, or string-like input range, to char type not directly 2096 supported by parse parses the first dchar of the source. 2097 2098 Returns: the first code point of the input range, converted 2099 to type T. 2100 2101 Throws: ConvException if the input range contains more than 2102 a single code point, or if the code point does not 2103 fit into a code unit of type T. 2104 */ 2105 private T toImpl(T, S)(S value) 2106 if (isSomeChar!T && !is(typeof(parse!T(value))) && 2107 is(typeof(parse!dchar(value)))) 2108 { 2109 import std.utf : encode; 2110 2111 immutable dchar codepoint = parse!dchar(value); 2112 if (!value.empty) 2113 throw new ConvException(convFormat("Cannot convert \"%s\" to %s because it " ~ 2114 "contains more than a single code point.", 2115 value, T.stringof)); 2116 T[dchar.sizeof / T.sizeof] decodedCodepoint; 2117 if (encode(decodedCodepoint, codepoint) != 1) 2118 throw new ConvException(convFormat("First code point '%s' of \"%s\" does not fit into a " ~ 2119 "single %s code unit", codepoint, value, T.stringof)); 2120 return decodedCodepoint[0]; 2121 } 2122 2123 @safe pure unittest 2124 { 2125 import std.exception : assertThrown; 2126 2127 assert(toImpl!wchar("a") == 'a'); 2128 2129 assert(toImpl!char("a"d) == 'a'); 2130 assert(toImpl!char("a"w) == 'a'); 2131 assert(toImpl!wchar("a"d) == 'a'); 2132 2133 assertThrown!ConvException(toImpl!wchar("ab")); 2134 assertThrown!ConvException(toImpl!char("😃"d)); 2135 } 2136 2137 /** 2138 Convert a value that is implicitly convertible to the enum base type 2139 into an Enum value. If the value does not match any enum member values 2140 a ConvException is thrown. 2141 Enums with floating-point or string base types are not supported. 2142 */ 2143 private T toImpl(T, S)(S value) 2144 if (is(T == enum) && !is(S == enum) 2145 && is(typeof(value == OriginalType!T.init)) 2146 && !isFloatingPoint!(OriginalType!T) && !isSomeString!(OriginalType!T)) 2147 { 2148 foreach (Member; EnumMembers!T) 2149 { 2150 if (Member == value) 2151 return Member; 2152 } 2153 throw new ConvException(convFormat("Value (%s) does not match any member value of enum '%s'", value, T.stringof)); 2154 } 2155 2156 @safe pure unittest 2157 { 2158 import std.exception; 2159 enum En8143 : int { A = 10, B = 20, C = 30, D = 20 } 2160 enum En8143[][] m3 = to!(En8143[][])([[10, 30], [30, 10]]); 2161 static assert(m3 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]); 2162 2163 En8143 en1 = to!En8143(10); 2164 assert(en1 == En8143.A); 2165 assertThrown!ConvException(to!En8143(5)); // matches none 2166 En8143[][] m1 = to!(En8143[][])([[10, 30], [30, 10]]); 2167 assert(m1 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]); 2168 } 2169 2170 // https://issues.dlang.org/show_bug.cgi?id=20539 2171 @safe pure unittest 2172 { 2173 import std.exception : assertNotThrown; 2174 2175 // To test that the bug is fixed it is required that the struct is static, 2176 // otherwise, the frame pointer makes the test pass even if the bug is not 2177 // fixed. 2178 2179 static struct A 2180 { 2181 auto opEquals(U)(U) 2182 { 2183 return true; 2184 } 2185 } 2186 2187 enum ColorA 2188 { 2189 red = A() 2190 } 2191 2192 assertNotThrown("xxx".to!ColorA); 2193 2194 // This is a guard for the future. 2195 2196 struct B 2197 { 2198 auto opEquals(U)(U) 2199 { 2200 return true; 2201 } 2202 } 2203 2204 enum ColorB 2205 { 2206 red = B() 2207 } 2208 2209 assertNotThrown("xxx".to!ColorB); 2210 } 2211 2212 /*************************************************************** 2213 Rounded conversion from floating point to integral. 2214 2215 Rounded conversions do not work with non-integral target types. 2216 */ 2217 2218 template roundTo(Target) 2219 { 2220 Target roundTo(Source)(Source value) 2221 { 2222 import core.math : abs = fabs; 2223 import std.math.exponential : log2; 2224 import std.math.rounding : trunc; 2225 2226 static assert(isFloatingPoint!Source); 2227 static assert(isIntegral!Target); 2228 2229 // If value >= 2 ^^ (real.mant_dig - 1), the number is an integer 2230 // and adding 0.5 won't work, but we allready know, that we do 2231 // not have to round anything. 2232 if (log2(abs(value)) >= real.mant_dig - 1) 2233 return to!Target(value); 2234 2235 return to!Target(trunc(value + (value < 0 ? -0.5L : 0.5L))); 2236 } 2237 } 2238 2239 /// 2240 @safe unittest 2241 { 2242 assert(roundTo!int(3.14) == 3); 2243 assert(roundTo!int(3.49) == 3); 2244 assert(roundTo!int(3.5) == 4); 2245 assert(roundTo!int(3.999) == 4); 2246 assert(roundTo!int(-3.14) == -3); 2247 assert(roundTo!int(-3.49) == -3); 2248 assert(roundTo!int(-3.5) == -4); 2249 assert(roundTo!int(-3.999) == -4); 2250 assert(roundTo!(const int)(to!(const double)(-3.999)) == -4); 2251 } 2252 2253 @safe unittest 2254 { 2255 import std.exception; 2256 // boundary values 2257 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint)) 2258 { 2259 assert(roundTo!Int(Int.min - 0.4L) == Int.min); 2260 assert(roundTo!Int(Int.max + 0.4L) == Int.max); 2261 assertThrown!ConvOverflowException(roundTo!Int(Int.min - 0.5L)); 2262 assertThrown!ConvOverflowException(roundTo!Int(Int.max + 0.5L)); 2263 } 2264 } 2265 2266 @safe unittest 2267 { 2268 import std.exception; 2269 assertThrown!ConvException(roundTo!int(float.init)); 2270 auto ex = collectException(roundTo!int(float.init)); 2271 assert(ex.msg == "Input was NaN"); 2272 } 2273 2274 // https://issues.dlang.org/show_bug.cgi?id=5232 2275 @safe pure unittest 2276 { 2277 static if (real.mant_dig >= 64) 2278 ulong maxOdd = ulong.max; 2279 else 2280 ulong maxOdd = (1UL << real.mant_dig) - 1; 2281 2282 real r1 = maxOdd; 2283 assert(roundTo!ulong(r1) == maxOdd); 2284 2285 real r2 = maxOdd - 1; 2286 assert(roundTo!ulong(r2) == maxOdd - 1); 2287 2288 real r3 = maxOdd / 2; 2289 assert(roundTo!ulong(r3) == maxOdd / 2); 2290 2291 real r4 = maxOdd / 2 + 1; 2292 assert(roundTo!ulong(r4) == maxOdd / 2 + 1); 2293 2294 // this is only an issue on computers where real == double 2295 long l = -((1L << double.mant_dig) - 1); 2296 double r5 = l; 2297 assert(roundTo!long(r5) == l); 2298 } 2299 2300 /** 2301 $(PANEL 2302 The `parse` family of functions works quite like the $(LREF to) 2303 family, except that: 2304 $(OL 2305 $(LI It only works with character ranges as input.) 2306 $(LI It takes the input by reference. This means that rvalues (such 2307 as string literals) are not accepted: use `to` instead.) 2308 $(LI It advances the input to the position following the conversion.) 2309 $(LI It does not throw if it could not convert the entire input.)) 2310 ) 2311 2312 This overload parses a `bool` from a character input range. 2313 2314 Params: 2315 Target = the boolean type to convert to 2316 source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 2317 doCount = the flag for deciding to report the number of consumed characters 2318 2319 Returns: 2320 $(UL 2321 $(LI A `bool` if `doCount` is set to `No.doCount`) 2322 $(LI A `tuple` containing a `bool` and a `size_t` if `doCount` is set to `Yes.doCount`)) 2323 2324 Throws: 2325 A $(LREF ConvException) if the range does not represent a `bool`. 2326 2327 Note: 2328 All character input range conversions using $(LREF to) are forwarded 2329 to `parse` and do not require lvalues. 2330 */ 2331 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source) 2332 if (is(immutable Target == immutable bool) && 2333 isInputRange!Source && 2334 isSomeChar!(ElementType!Source)) 2335 { 2336 import std.ascii : toLower; 2337 2338 static if (isNarrowString!Source) 2339 { 2340 import std.string : representation; 2341 auto s = source.representation; 2342 } 2343 else 2344 { 2345 alias s = source; 2346 } 2347 2348 if (!s.empty) 2349 { 2350 auto c1 = toLower(s.front); 2351 bool result = c1 == 't'; 2352 if (result || c1 == 'f') 2353 { 2354 s.popFront(); 2355 foreach (c; result ? "rue" : "alse") 2356 { 2357 if (s.empty || toLower(s.front) != c) 2358 goto Lerr; 2359 s.popFront(); 2360 } 2361 2362 static if (isNarrowString!Source) 2363 source = cast(Source) s; 2364 2365 static if (doCount) 2366 { 2367 if (result) 2368 return tuple!("data", "count")(result, 4); 2369 return tuple!("data", "count")(result, 5); 2370 } 2371 else 2372 { 2373 return result; 2374 } 2375 } 2376 } 2377 Lerr: 2378 throw parseError("bool should be case-insensitive 'true' or 'false'"); 2379 } 2380 2381 /// 2382 @safe unittest 2383 { 2384 import std.typecons : Flag, Yes, No; 2385 auto s = "true"; 2386 bool b = parse!bool(s); 2387 assert(b); 2388 auto s2 = "true"; 2389 bool b2 = parse!(bool, string, No.doCount)(s2); 2390 assert(b2); 2391 auto s3 = "true"; 2392 auto b3 = parse!(bool, string, Yes.doCount)(s3); 2393 assert(b3.data && b3.count == 4); 2394 auto s4 = "falSE"; 2395 auto b4 = parse!(bool, string, Yes.doCount)(s4); 2396 assert(!b4.data && b4.count == 5); 2397 } 2398 2399 @safe unittest 2400 { 2401 import std.algorithm.comparison : equal; 2402 import std.exception; 2403 struct InputString 2404 { 2405 string _s; 2406 @property auto front() { return _s.front; } 2407 @property bool empty() { return _s.empty; } 2408 void popFront() { _s.popFront(); } 2409 } 2410 2411 auto s = InputString("trueFALSETrueFalsetRUEfALSE"); 2412 assert(parse!bool(s) == true); 2413 assert(s.equal("FALSETrueFalsetRUEfALSE")); 2414 assert(parse!bool(s) == false); 2415 assert(s.equal("TrueFalsetRUEfALSE")); 2416 assert(parse!bool(s) == true); 2417 assert(s.equal("FalsetRUEfALSE")); 2418 assert(parse!bool(s) == false); 2419 assert(s.equal("tRUEfALSE")); 2420 assert(parse!bool(s) == true); 2421 assert(s.equal("fALSE")); 2422 assert(parse!bool(s) == false); 2423 assert(s.empty); 2424 2425 foreach (ss; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""]) 2426 { 2427 s = InputString(ss); 2428 assertThrown!ConvException(parse!bool(s)); 2429 } 2430 } 2431 2432 /** 2433 Parses an integer from a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives). 2434 2435 Params: 2436 Target = the integral type to convert to 2437 s = the lvalue of an input range 2438 doCount = the flag for deciding to report the number of consumed characters 2439 2440 Returns: 2441 $(UL 2442 $(LI A number of type `Target` if `doCount` is set to `No.doCount`) 2443 $(LI A `tuple` containing a number of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 2444 2445 Throws: 2446 A $(LREF ConvException) If an overflow occurred during conversion or 2447 if no character of the input was meaningfully converted. 2448 */ 2449 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref scope Source s) 2450 if (isIntegral!Target && !is(Target == enum) && 2451 isSomeChar!(ElementType!Source)) 2452 { 2453 static if (Target.sizeof < int.sizeof) 2454 { 2455 // smaller types are handled like integers 2456 auto v = .parse!(Select!(Target.min < 0, int, uint), Source, Yes.doCount)(s); 2457 auto result = (() @trusted => cast (Target) v.data)(); 2458 if (result == v.data) 2459 { 2460 static if (doCount) 2461 { 2462 return tuple!("data", "count")(result, v.count); 2463 } 2464 else 2465 { 2466 return result; 2467 } 2468 } 2469 throw new ConvOverflowException("Overflow in integral conversion"); 2470 } 2471 else 2472 { 2473 // int or larger types 2474 2475 static if (Target.min < 0) 2476 bool sign = false; 2477 else 2478 enum bool sign = false; 2479 2480 enum char maxLastDigit = Target.min < 0 ? 7 : 5; 2481 uint c; 2482 2483 static if (isNarrowString!Source) 2484 { 2485 import std.string : representation; 2486 auto source = s.representation; 2487 } 2488 else 2489 { 2490 alias source = s; 2491 } 2492 2493 static if (doCount) 2494 size_t count = 0; 2495 2496 if (source.empty) 2497 goto Lerr; 2498 2499 c = source.front; 2500 2501 static if (Target.min < 0) 2502 { 2503 switch (c) 2504 { 2505 case '-': 2506 sign = true; 2507 goto case '+'; 2508 case '+': 2509 static if (doCount) 2510 ++count; 2511 source.popFront(); 2512 2513 if (source.empty) 2514 goto Lerr; 2515 2516 c = source.front; 2517 2518 break; 2519 2520 default: 2521 break; 2522 } 2523 } 2524 c -= '0'; 2525 if (c <= 9) 2526 { 2527 Target v = cast(Target) c; 2528 2529 static if (doCount) 2530 ++count; 2531 source.popFront(); 2532 2533 while (!source.empty) 2534 { 2535 c = cast(typeof(c)) (source.front - '0'); 2536 2537 if (c > 9) 2538 break; 2539 2540 if (v >= 0 && (v < Target.max/10 || 2541 (v == Target.max/10 && c <= maxLastDigit + sign))) 2542 { 2543 // Note: `v` can become negative here in case of parsing 2544 // the most negative value: 2545 v = cast(Target) (v * 10 + c); 2546 static if (doCount) 2547 ++count; 2548 source.popFront(); 2549 } 2550 else 2551 throw new ConvOverflowException("Overflow in integral conversion"); 2552 } 2553 2554 if (sign) 2555 v = -v; 2556 2557 static if (isNarrowString!Source) 2558 s = s[$-source.length..$]; 2559 2560 static if (doCount) 2561 { 2562 return tuple!("data", "count")(v, count); 2563 } 2564 else 2565 { 2566 return v; 2567 } 2568 } 2569 Lerr: 2570 static if (isNarrowString!Source) 2571 throw convError!(Source, Target)(cast(Source) source); 2572 else 2573 throw convError!(Source, Target)(source); 2574 } 2575 } 2576 2577 /// 2578 @safe pure unittest 2579 { 2580 import std.typecons : Flag, Yes, No; 2581 string s = "123"; 2582 auto a = parse!int(s); 2583 assert(a == 123); 2584 2585 string s1 = "123"; 2586 auto a1 = parse!(int, string, Yes.doCount)(s1); 2587 assert(a1.data == 123 && a1.count == 3); 2588 } 2589 2590 /// 2591 @safe pure unittest 2592 { 2593 import std.string : tr; 2594 import std.typecons : Flag, Yes, No; 2595 string test = "123 \t 76.14"; 2596 auto a = parse!uint(test); 2597 assert(a == 123); 2598 assert(test == " \t 76.14"); // parse bumps string 2599 test = tr(test, " \t\n\r", "", "d"); // skip ws 2600 assert(test == "76.14"); 2601 auto b = parse!double(test); 2602 assert(b == 76.14); 2603 assert(test == ""); 2604 2605 string test2 = "123 \t 76.14"; 2606 auto a2 = parse!(uint, string, Yes.doCount)(test2); 2607 assert(a2.data == 123 && a2.count == 3); 2608 assert(test2 == " \t 76.14");// parse bumps string 2609 test2 = tr(test2, " \t\n\r", "", "d"); // skip ws 2610 assert(test2 == "76.14"); 2611 auto b2 = parse!(double, string, Yes.doCount)(test2); 2612 assert(b2.data == 76.14 && b2.count == 5); 2613 assert(test2 == ""); 2614 2615 } 2616 2617 @safe pure unittest 2618 { 2619 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 2620 { 2621 { 2622 assert(to!Int("0") == 0); 2623 2624 static if (isSigned!Int) 2625 { 2626 assert(to!Int("+0") == 0); 2627 assert(to!Int("-0") == 0); 2628 } 2629 } 2630 2631 static if (Int.sizeof >= byte.sizeof) 2632 { 2633 assert(to!Int("6") == 6); 2634 assert(to!Int("23") == 23); 2635 assert(to!Int("68") == 68); 2636 assert(to!Int("127") == 0x7F); 2637 2638 static if (isUnsigned!Int) 2639 { 2640 assert(to!Int("255") == 0xFF); 2641 } 2642 static if (isSigned!Int) 2643 { 2644 assert(to!Int("+6") == 6); 2645 assert(to!Int("+23") == 23); 2646 assert(to!Int("+68") == 68); 2647 assert(to!Int("+127") == 0x7F); 2648 2649 assert(to!Int("-6") == -6); 2650 assert(to!Int("-23") == -23); 2651 assert(to!Int("-68") == -68); 2652 assert(to!Int("-128") == -128); 2653 } 2654 } 2655 2656 static if (Int.sizeof >= short.sizeof) 2657 { 2658 assert(to!Int("468") == 468); 2659 assert(to!Int("32767") == 0x7FFF); 2660 2661 static if (isUnsigned!Int) 2662 { 2663 assert(to!Int("65535") == 0xFFFF); 2664 } 2665 static if (isSigned!Int) 2666 { 2667 assert(to!Int("+468") == 468); 2668 assert(to!Int("+32767") == 0x7FFF); 2669 2670 assert(to!Int("-468") == -468); 2671 assert(to!Int("-32768") == -32768); 2672 } 2673 } 2674 2675 static if (Int.sizeof >= int.sizeof) 2676 { 2677 assert(to!Int("2147483647") == 0x7FFFFFFF); 2678 2679 static if (isUnsigned!Int) 2680 { 2681 assert(to!Int("4294967295") == 0xFFFFFFFF); 2682 } 2683 2684 static if (isSigned!Int) 2685 { 2686 assert(to!Int("+2147483647") == 0x7FFFFFFF); 2687 2688 assert(to!Int("-2147483648") == -2147483648); 2689 } 2690 } 2691 2692 static if (Int.sizeof >= long.sizeof) 2693 { 2694 assert(to!Int("9223372036854775807") == 0x7FFFFFFFFFFFFFFF); 2695 2696 static if (isUnsigned!Int) 2697 { 2698 assert(to!Int("18446744073709551615") == 0xFFFFFFFFFFFFFFFF); 2699 } 2700 2701 static if (isSigned!Int) 2702 { 2703 assert(to!Int("+9223372036854775807") == 0x7FFFFFFFFFFFFFFF); 2704 2705 assert(to!Int("-9223372036854775808") == 0x8000000000000000); 2706 } 2707 } 2708 } 2709 } 2710 2711 @safe pure unittest 2712 { 2713 import std.exception; 2714 2715 immutable string[] errors = 2716 [ 2717 "", 2718 "-", 2719 "+", 2720 "-+", 2721 " ", 2722 " 0", 2723 "0 ", 2724 "- 0", 2725 "1-", 2726 "xx", 2727 "123h", 2728 "-+1", 2729 "--1", 2730 "+-1", 2731 "++1", 2732 ]; 2733 2734 immutable string[] unsignedErrors = 2735 [ 2736 "+5", 2737 "-78", 2738 ]; 2739 2740 // parsing error check 2741 static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 2742 { 2743 foreach (j, s; errors) 2744 assertThrown!ConvException(to!Int(s)); 2745 2746 // parse!SomeUnsigned cannot parse head sign. 2747 static if (isUnsigned!Int) 2748 { 2749 foreach (j, s; unsignedErrors) 2750 assertThrown!ConvException(to!Int(s)); 2751 } 2752 } 2753 2754 immutable string[] positiveOverflowErrors = 2755 [ 2756 "128", // > byte.max 2757 "256", // > ubyte.max 2758 "32768", // > short.max 2759 "65536", // > ushort.max 2760 "2147483648", // > int.max 2761 "4294967296", // > uint.max 2762 "9223372036854775808", // > long.max 2763 "18446744073709551616", // > ulong.max 2764 ]; 2765 // positive overflow check 2766 static foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) 2767 { 2768 foreach (j, s; positiveOverflowErrors[i..$]) 2769 assertThrown!ConvOverflowException(to!Int(s)); 2770 } 2771 2772 immutable string[] negativeOverflowErrors = 2773 [ 2774 "-129", // < byte.min 2775 "-32769", // < short.min 2776 "-2147483649", // < int.min 2777 "-9223372036854775809", // < long.min 2778 ]; 2779 // negative overflow check 2780 static foreach (i, Int; AliasSeq!(byte, short, int, long)) 2781 { 2782 foreach (j, s; negativeOverflowErrors[i..$]) 2783 assertThrown!ConvOverflowException(to!Int(s)); 2784 } 2785 } 2786 2787 @safe pure unittest 2788 { 2789 void checkErrMsg(string input, dchar charInMsg, dchar charNotInMsg) 2790 { 2791 try 2792 { 2793 int x = input.to!int(); 2794 assert(false, "Invalid conversion did not throw"); 2795 } 2796 catch (ConvException e) 2797 { 2798 // Ensure error message contains failing character, not the character 2799 // beyond. 2800 import std.algorithm.searching : canFind; 2801 assert( e.msg.canFind(charInMsg) && 2802 !e.msg.canFind(charNotInMsg)); 2803 } 2804 catch (Exception e) 2805 { 2806 assert(false, "Did not throw ConvException"); 2807 } 2808 } 2809 checkErrMsg("@$", '@', '$'); 2810 checkErrMsg("@$123", '@', '$'); 2811 checkErrMsg("1@$23", '@', '$'); 2812 checkErrMsg("1@$", '@', '$'); 2813 checkErrMsg("1@$2", '@', '$'); 2814 checkErrMsg("12@$", '@', '$'); 2815 } 2816 2817 @safe pure unittest 2818 { 2819 import std.exception; 2820 assertCTFEable!({ string s = "1234abc"; assert(parse! int(s) == 1234 && s == "abc"); }); 2821 assertCTFEable!({ string s = "-1234abc"; assert(parse! int(s) == -1234 && s == "abc"); }); 2822 assertCTFEable!({ string s = "1234abc"; assert(parse!uint(s) == 1234 && s == "abc"); }); 2823 2824 assertCTFEable!({ string s = "1234abc"; assert(parse!( int, string, Yes.doCount)(s) == 2825 tuple( 1234, 4) && s == "abc"); }); 2826 assertCTFEable!({ string s = "-1234abc"; assert(parse!( int, string, Yes.doCount)(s) == 2827 tuple(-1234, 5) && s == "abc"); }); 2828 assertCTFEable!({ string s = "1234abc"; assert(parse!(uint, string, Yes.doCount)(s) == 2829 tuple( 1234 ,4) && s == "abc"); }); 2830 } 2831 2832 // https://issues.dlang.org/show_bug.cgi?id=13931 2833 @safe pure unittest 2834 { 2835 import std.exception; 2836 2837 assertThrown!ConvOverflowException("-21474836480".to!int()); 2838 assertThrown!ConvOverflowException("-92233720368547758080".to!long()); 2839 } 2840 2841 // https://issues.dlang.org/show_bug.cgi?id=14396 2842 @safe pure unittest 2843 { 2844 struct StrInputRange 2845 { 2846 this (string s) { str = s; } 2847 char front() const @property { return str[front_index]; } 2848 char popFront() { return str[front_index++]; } 2849 bool empty() const @property { return str.length <= front_index; } 2850 string str; 2851 size_t front_index = 0; 2852 } 2853 auto input = StrInputRange("777"); 2854 assert(parse!int(input) == 777); 2855 2856 auto input2 = StrInputRange("777"); 2857 assert(parse!(int, StrInputRange, Yes.doCount)(input2) == tuple(777, 3)); 2858 } 2859 2860 // https://issues.dlang.org/show_bug.cgi?id=9621 2861 @safe pure unittest 2862 { 2863 string s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]"; 2864 assert(parse!(string[])(s1) == ["a", "\0", "!", "!8"]); 2865 2866 s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]"; 2867 auto len = s1.length; 2868 assert(parse!(string[], string, Yes.doCount)(s1) == tuple(["a", "\0", "!", "!8"], len)); 2869 } 2870 2871 /// ditto 2872 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source, uint radix) 2873 if (isIntegral!Target && !is(Target == enum) && 2874 isSomeChar!(ElementType!Source)) 2875 in 2876 { 2877 assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]"); 2878 } 2879 do 2880 { 2881 import core.checkedint : mulu, addu; 2882 import std.exception : enforce; 2883 2884 if (radix == 10) 2885 { 2886 return parse!(Target, Source, doCount)(source); 2887 } 2888 2889 enforce!ConvException(!source.empty, "s must not be empty in integral parse"); 2890 2891 immutable uint beyond = (radix < 10 ? '0' : 'a'-10) + radix; 2892 Target v = 0; 2893 2894 static if (isNarrowString!Source) 2895 { 2896 import std.string : representation; 2897 scope s = source.representation; 2898 } 2899 else 2900 { 2901 alias s = source; 2902 } 2903 2904 static if (doCount) 2905 size_t count = 0; 2906 auto found = false; 2907 do 2908 { 2909 uint c = s.front; 2910 if (c < '0') 2911 break; 2912 if (radix < 10) 2913 { 2914 if (c >= beyond) 2915 break; 2916 } 2917 else 2918 { 2919 if (c > '9') 2920 { 2921 c |= 0x20;//poorman's tolower 2922 if (c < 'a' || c >= beyond) 2923 break; 2924 c -= 'a'-10-'0'; 2925 } 2926 } 2927 2928 bool overflow = false; 2929 auto nextv = v.mulu(radix, overflow).addu(c - '0', overflow); 2930 enforce!ConvOverflowException(!overflow && nextv <= Target.max, "Overflow in integral conversion"); 2931 v = cast(Target) nextv; 2932 static if (doCount) 2933 ++count; 2934 s.popFront(); 2935 found = true; 2936 } while (!s.empty); 2937 2938 if (!found) 2939 { 2940 static if (isNarrowString!Source) 2941 throw convError!(Source, Target)(cast(Source) source); 2942 else 2943 throw convError!(Source, Target)(source); 2944 } 2945 2946 static if (isNarrowString!Source) 2947 source = source[$ - s.length .. $]; 2948 2949 static if (doCount) 2950 { 2951 return tuple!("data", "count")(v, count); 2952 } 2953 else 2954 { 2955 return v; 2956 } 2957 } 2958 2959 @safe pure unittest 2960 { 2961 string s; // parse doesn't accept rvalues 2962 foreach (i; 2 .. 37) 2963 { 2964 assert(parse!int(s = "0", i) == 0); 2965 assert(parse!int(s = "1", i) == 1); 2966 assert(parse!byte(s = "10", i) == i); 2967 assert(parse!(int, string, Yes.doCount)(s = "0", i) == tuple(0, 1)); 2968 assert(parse!(int, string, Yes.doCount)(s = "1", i) == tuple(1, 1)); 2969 assert(parse!(byte, string, Yes.doCount)(s = "10", i) == tuple(i, 2)); 2970 } 2971 2972 assert(parse!int(s = "0011001101101", 2) == 0b0011001101101); 2973 assert(parse!int(s = "765", 8) == octal!765); 2974 assert(parse!int(s = "000135", 8) == octal!"135"); 2975 assert(parse!int(s = "fCDe", 16) == 0xfcde); 2976 2977 // https://issues.dlang.org/show_bug.cgi?id=6609 2978 assert(parse!int(s = "-42", 10) == -42); 2979 2980 assert(parse!ubyte(s = "ff", 16) == 0xFF); 2981 } 2982 2983 // https://issues.dlang.org/show_bug.cgi?id=7302 2984 @safe pure unittest 2985 { 2986 import std.range : cycle; 2987 auto r = cycle("2A!"); 2988 auto u = parse!uint(r, 16); 2989 assert(u == 42); 2990 assert(r.front == '!'); 2991 2992 auto r2 = cycle("2A!"); 2993 auto u2 = parse!(uint, typeof(r2), Yes.doCount)(r2, 16); 2994 assert(u2.data == 42 && u2.count == 2); 2995 assert(r2.front == '!'); 2996 } 2997 2998 // https://issues.dlang.org/show_bug.cgi?id=13163 2999 @safe pure unittest 3000 { 3001 import std.exception; 3002 foreach (s; ["fff", "123"]) 3003 assertThrown!ConvOverflowException(s.parse!ubyte(16)); 3004 } 3005 3006 // https://issues.dlang.org/show_bug.cgi?id=17282 3007 @safe pure unittest 3008 { 3009 auto str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n"; 3010 assert(parse!uint(str) == 0); 3011 3012 str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n"; 3013 assert(parse!(uint, string, Yes.doCount)(str) == tuple(0, 1)); 3014 } 3015 3016 // https://issues.dlang.org/show_bug.cgi?id=18248 3017 @safe pure unittest 3018 { 3019 import std.exception : assertThrown; 3020 3021 auto str = ";"; 3022 assertThrown(str.parse!uint(16)); 3023 assertThrown(str.parse!(uint, string, Yes.doCount)(16)); 3024 } 3025 3026 /** 3027 * Parses an `enum` type from a string representing an enum member name. 3028 * 3029 * Params: 3030 * Target = the `enum` type to convert to 3031 * s = the lvalue of the range to _parse 3032 * doCount = the flag for deciding to report the number of consumed characters 3033 * 3034 * Returns: 3035 $(UL 3036 * $(LI An `enum` of type `Target` if `doCount` is set to `No.doCount`) 3037 * $(LI A `tuple` containing an `enum` of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 3038 * 3039 * Throws: 3040 * A $(LREF ConvException) if type `Target` does not have a member 3041 * represented by `s`. 3042 */ 3043 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 3044 if (is(Target == enum) && isSomeString!Source && !is(Source == enum)) 3045 { 3046 import std.algorithm.searching : startsWith; 3047 import std.traits : Unqual, EnumMembers; 3048 3049 Unqual!Target result; 3050 size_t longest_match = 0; 3051 3052 foreach (i, e; EnumMembers!Target) 3053 { 3054 auto ident = __traits(allMembers, Target)[i]; 3055 if (longest_match < ident.length && s.startsWith(ident)) 3056 { 3057 result = e; 3058 longest_match = ident.length ; 3059 } 3060 } 3061 3062 if (longest_match > 0) 3063 { 3064 s = s[longest_match .. $]; 3065 static if (doCount) 3066 { 3067 return tuple!("data", "count")(result, longest_match); 3068 } 3069 else 3070 { 3071 return result; 3072 } 3073 } 3074 3075 throw new ConvException( 3076 Target.stringof ~ " does not have a member named '" 3077 ~ to!string(s) ~ "'"); 3078 } 3079 3080 /// 3081 @safe unittest 3082 { 3083 import std.typecons : Flag, Yes, No, tuple; 3084 enum EnumType : bool { a = true, b = false, c = a } 3085 3086 auto str = "a"; 3087 assert(parse!EnumType(str) == EnumType.a); 3088 auto str2 = "a"; 3089 assert(parse!(EnumType, string, No.doCount)(str2) == EnumType.a); 3090 auto str3 = "a"; 3091 assert(parse!(EnumType, string, Yes.doCount)(str3) == tuple(EnumType.a, 1)); 3092 3093 } 3094 3095 @safe unittest 3096 { 3097 import std.exception; 3098 3099 enum EB : bool { a = true, b = false, c = a } 3100 enum EU { a, b, c } 3101 enum EI { a = -1, b = 0, c = 1 } 3102 enum EF : real { a = 1.414, b = 1.732, c = 2.236 } 3103 enum EC : char { a = 'a', b = 'b', c = 'c' } 3104 enum ES : string { a = "aaa", b = "bbb", c = "ccc" } 3105 3106 static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) 3107 { 3108 assert(to!E("a"c) == E.a); 3109 assert(to!E("b"w) == E.b); 3110 assert(to!E("c"d) == E.c); 3111 3112 assert(to!(const E)("a") == E.a); 3113 assert(to!(immutable E)("a") == E.a); 3114 assert(to!(shared E)("a") == E.a); 3115 3116 assertThrown!ConvException(to!E("d")); 3117 } 3118 } 3119 3120 // https://issues.dlang.org/show_bug.cgi?id=4744 3121 @safe pure unittest 3122 { 3123 enum A { member1, member11, member111 } 3124 assert(to!A("member1" ) == A.member1 ); 3125 assert(to!A("member11" ) == A.member11 ); 3126 assert(to!A("member111") == A.member111); 3127 auto s = "member1111"; 3128 assert(parse!A(s) == A.member111 && s == "1"); 3129 auto s2 = "member1111"; 3130 assert(parse!(A, string, No.doCount)(s2) == A.member111 && s2 == "1"); 3131 auto s3 = "member1111"; 3132 assert(parse!(A, string, Yes.doCount)(s3) == tuple(A.member111, 9) && s3 == "1"); 3133 } 3134 3135 /** 3136 * Parses a floating point number from a character range. 3137 * 3138 * Params: 3139 * Target = a floating point type 3140 * source = the lvalue of the range to _parse 3141 * doCount = the flag for deciding to report the number of consumed characters 3142 * 3143 * Returns: 3144 $(UL 3145 * $(LI A floating point number of type `Target` if `doCount` is set to `No.doCount`) 3146 * $(LI A `tuple` containing a floating point number of·type `Target` and a `size_t` 3147 * if `doCount` is set to `Yes.doCount`)) 3148 * 3149 * Throws: 3150 * A $(LREF ConvException) if `source` is empty, if no number could be 3151 * parsed, or if an overflow occurred. 3152 */ 3153 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source) 3154 if (isFloatingPoint!Target && !is(Target == enum) && 3155 isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum)) 3156 { 3157 import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; 3158 import std.exception : enforce; 3159 3160 static if (isNarrowString!Source) 3161 { 3162 import std.string : representation; 3163 scope p = source.representation; 3164 } 3165 else 3166 { 3167 alias p = source; 3168 } 3169 3170 void advanceSource() 3171 { 3172 static if (isNarrowString!Source) 3173 source = source[$ - p.length .. $]; 3174 } 3175 3176 static immutable real[14] negtab = 3177 [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L, 3178 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ]; 3179 static immutable real[13] postab = 3180 [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L, 3181 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ]; 3182 3183 ConvException bailOut()(string msg = null, string fn = __FILE__, size_t ln = __LINE__) 3184 { 3185 if (msg == null) 3186 msg = "Floating point conversion error"; 3187 return new ConvException(text(msg, " for input \"", source, "\"."), fn, ln); 3188 } 3189 3190 enforce(!p.empty, bailOut()); 3191 3192 size_t count = 0; 3193 bool sign = false; 3194 switch (p.front) 3195 { 3196 case '-': 3197 sign = true; 3198 static if (doCount) ++count; 3199 p.popFront(); 3200 enforce(!p.empty, bailOut()); 3201 if (toLower(p.front) == 'i') 3202 goto case 'i'; 3203 break; 3204 case '+': 3205 static if (doCount) ++count; 3206 p.popFront(); 3207 enforce(!p.empty, bailOut()); 3208 break; 3209 case 'i': case 'I': 3210 // inf 3211 static if (doCount) ++count; 3212 p.popFront(); 3213 enforce(!p.empty && toUpper(p.front) == 'N', 3214 bailOut("error converting input to floating point")); 3215 static if (doCount) ++count; 3216 p.popFront(); 3217 enforce(!p.empty && toUpper(p.front) == 'F', 3218 bailOut("error converting input to floating point")); 3219 // skip past the last 'f' 3220 static if (doCount) ++count; 3221 p.popFront(); 3222 advanceSource(); 3223 static if (doCount) 3224 { 3225 return tuple!("data", "count")(sign ? -Target.infinity : Target.infinity, count); 3226 } 3227 else 3228 { 3229 return sign ? -Target.infinity : Target.infinity; 3230 } 3231 default: {} 3232 } 3233 3234 bool isHex = false; 3235 bool startsWithZero = p.front == '0'; 3236 if (startsWithZero) 3237 { 3238 static if (doCount) ++count; 3239 p.popFront(); 3240 if (p.empty) 3241 { 3242 advanceSource(); 3243 static if (doCount) 3244 { 3245 return tuple!("data", "count")(cast (Target) (sign ? -0.0 : 0.0), count); 3246 } 3247 else 3248 { 3249 return sign ? -0.0 : 0.0; 3250 } 3251 } 3252 3253 isHex = p.front == 'x' || p.front == 'X'; 3254 if (isHex) 3255 { 3256 static if (doCount) ++count; 3257 p.popFront(); 3258 } 3259 } 3260 else if (toLower(p.front) == 'n') 3261 { 3262 // nan 3263 static if (doCount) ++count; 3264 p.popFront(); 3265 enforce(!p.empty && toUpper(p.front) == 'A', 3266 bailOut("error converting input to floating point")); 3267 static if (doCount) ++count; 3268 p.popFront(); 3269 enforce(!p.empty && toUpper(p.front) == 'N', 3270 bailOut("error converting input to floating point")); 3271 // skip past the last 'n' 3272 static if (doCount) ++count; 3273 p.popFront(); 3274 advanceSource(); 3275 static if (doCount) 3276 { 3277 return tuple!("data", "count")(Target.nan, count); 3278 } 3279 else 3280 { 3281 return typeof(return).nan; 3282 } 3283 } 3284 3285 /* 3286 * The following algorithm consists of 2 steps: 3287 * 1) parseDigits processes the textual input into msdec and possibly 3288 * lsdec/msscale variables, followed by the exponent parser which sets 3289 * exp below. 3290 * Hex: input is 0xaaaaa...p+000... where aaaa is the mantissa in hex 3291 * and 000 is the exponent in decimal format with base 2. 3292 * Decimal: input is 0.00333...p+000... where 0.0033 is the mantissa 3293 * in decimal and 000 is the exponent in decimal format with base 10. 3294 * 2) Convert msdec/lsdec and exp into native real format 3295 */ 3296 3297 real ldval = 0.0; 3298 char dot = 0; /* if decimal point has been seen */ 3299 int exp = 0; 3300 ulong msdec = 0, lsdec = 0; 3301 ulong msscale = 1; 3302 bool sawDigits; 3303 3304 enum { hex, decimal } 3305 3306 // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits 3307 void parseDigits(alias FloatFormat)() 3308 { 3309 static if (FloatFormat == hex) 3310 { 3311 enum uint base = 16; 3312 enum ulong msscaleMax = 0x1000_0000_0000_0000UL; // largest power of 16 a ulong holds 3313 enum ubyte expIter = 4; // iterate the base-2 exponent by 4 for every hex digit 3314 alias checkDigit = isHexDigit; 3315 /* 3316 * convert letter to binary representation: First clear bit 3317 * to convert lower space chars to upperspace, then -('A'-10) 3318 * converts letter A to 10, letter B to 11, ... 3319 */ 3320 alias convertDigit = (int x) => isAlpha(x) ? ((x & ~0x20) - ('A' - 10)) : x - '0'; 3321 sawDigits = false; 3322 } 3323 else static if (FloatFormat == decimal) 3324 { 3325 enum uint base = 10; 3326 enum ulong msscaleMax = 10_000_000_000_000_000_000UL; // largest power of 10 a ulong holds 3327 enum ubyte expIter = 1; // iterate the base-10 exponent once for every decimal digit 3328 alias checkDigit = isDigit; 3329 alias convertDigit = (int x) => x - '0'; 3330 // Used to enforce that any mantissa digits are present 3331 sawDigits = startsWithZero; 3332 } 3333 else 3334 static assert(false, "Unrecognized floating-point format used."); 3335 3336 while (!p.empty) 3337 { 3338 int i = p.front; 3339 while (checkDigit(i)) 3340 { 3341 sawDigits = true; /* must have at least 1 digit */ 3342 3343 i = convertDigit(i); 3344 3345 if (msdec < (ulong.max - base)/base) 3346 { 3347 // For base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0 3348 msdec = msdec * base + i; 3349 } 3350 else if (msscale < msscaleMax) 3351 { 3352 lsdec = lsdec * base + i; 3353 msscale *= base; 3354 } 3355 else 3356 { 3357 exp += expIter; 3358 } 3359 exp -= dot; 3360 static if (doCount) ++count; 3361 p.popFront(); 3362 if (p.empty) 3363 break; 3364 i = p.front; 3365 if (i == '_') 3366 { 3367 static if (doCount) ++count; 3368 p.popFront(); 3369 if (p.empty) 3370 break; 3371 i = p.front; 3372 } 3373 } 3374 if (i == '.' && !dot) 3375 { 3376 static if (doCount) ++count; 3377 p.popFront(); 3378 dot += expIter; 3379 } 3380 else 3381 break; 3382 } 3383 3384 // Have we seen any mantissa digits so far? 3385 enforce(sawDigits, bailOut("no digits seen")); 3386 static if (FloatFormat == hex) 3387 enforce(!p.empty && (p.front == 'p' || p.front == 'P'), 3388 bailOut("Floating point parsing: exponent is required")); 3389 } 3390 3391 if (isHex) 3392 parseDigits!hex; 3393 else 3394 parseDigits!decimal; 3395 3396 if (isHex || (!p.empty && (p.front == 'e' || p.front == 'E'))) 3397 { 3398 char sexp = 0; 3399 int e = 0; 3400 3401 static if (doCount) ++count; 3402 p.popFront(); 3403 enforce(!p.empty, new ConvException("Unexpected end of input")); 3404 switch (p.front) 3405 { 3406 case '-': sexp++; 3407 goto case; 3408 case '+': static if (doCount) ++count; 3409 p.popFront(); 3410 break; 3411 default: {} 3412 } 3413 sawDigits = false; 3414 while (!p.empty && isDigit(p.front)) 3415 { 3416 if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow 3417 { 3418 e = e * 10 + p.front - '0'; 3419 } 3420 static if (doCount) ++count; 3421 p.popFront(); 3422 sawDigits = true; 3423 } 3424 exp += (sexp) ? -e : e; 3425 enforce(sawDigits, new ConvException("No digits seen.")); 3426 } 3427 3428 ldval = msdec; 3429 if (msscale != 1) /* if stuff was accumulated in lsdec */ 3430 ldval = ldval * msscale + lsdec; 3431 if (isHex) 3432 { 3433 import core.math : ldexp; 3434 3435 // Exponent is power of 2, not power of 10 3436 ldval = ldexp(ldval,exp); 3437 } 3438 else if (ldval) 3439 { 3440 uint u = 0; 3441 int pow = 4096; 3442 3443 while (exp > 0) 3444 { 3445 while (exp >= pow) 3446 { 3447 ldval *= postab[u]; 3448 exp -= pow; 3449 } 3450 pow >>= 1; 3451 u++; 3452 } 3453 while (exp < 0) 3454 { 3455 while (exp <= -pow) 3456 { 3457 ldval *= negtab[u]; 3458 enforce(ldval != 0, new ConvException("Range error")); 3459 exp += pow; 3460 } 3461 pow >>= 1; 3462 u++; 3463 } 3464 } 3465 3466 Target result = cast(Target) (sign ? -ldval : ldval); 3467 3468 // if overflow occurred 3469 import std.math.traits : isFinite; 3470 enforce(isFinite(result), new ConvException("Range error")); 3471 3472 advanceSource(); 3473 static if (doCount) 3474 { 3475 return tuple!("data", "count")(result, count); 3476 } 3477 else 3478 { 3479 return result; 3480 } 3481 } 3482 3483 3484 /// 3485 @safe unittest 3486 { 3487 import std.math.operations : isClose; 3488 import std.math.traits : isNaN, isInfinity; 3489 import std.typecons : Flag, Yes, No; 3490 auto str = "123.456"; 3491 assert(parse!double(str).isClose(123.456)); 3492 auto str2 = "123.456"; 3493 assert(parse!(double, string, No.doCount)(str2).isClose(123.456)); 3494 auto str3 = "123.456"; 3495 auto r = parse!(double, string, Yes.doCount)(str3); 3496 assert(r.data.isClose(123.456)); 3497 assert(r.count == 7); 3498 auto str4 = "-123.456"; 3499 r = parse!(double, string, Yes.doCount)(str4); 3500 assert(r.data.isClose(-123.456)); 3501 assert(r.count == 8); 3502 auto str5 = "+123.456"; 3503 r = parse!(double, string, Yes.doCount)(str5); 3504 assert(r.data.isClose(123.456)); 3505 assert(r.count == 8); 3506 auto str6 = "inf0"; 3507 r = parse!(double, string, Yes.doCount)(str6); 3508 assert(isInfinity(r.data) && r.count == 3 && str6 == "0"); 3509 auto str7 = "-0"; 3510 auto r2 = parse!(float, string, Yes.doCount)(str7); 3511 assert(r2.data.isClose(0.0) && r2.count == 2); 3512 auto str8 = "nan"; 3513 auto r3 = parse!(real, string, Yes.doCount)(str8); 3514 assert(isNaN(r3.data) && r3.count == 3); 3515 } 3516 3517 @safe unittest 3518 { 3519 import std.exception; 3520 import std.math.traits : isNaN, isInfinity; 3521 import std.math.algebraic : fabs; 3522 3523 // Compare reals with given precision 3524 bool feq(in real rx, in real ry, in real precision = 0.000001L) 3525 { 3526 if (rx == ry) 3527 return 1; 3528 3529 if (isNaN(rx)) 3530 return cast(bool) isNaN(ry); 3531 3532 if (isNaN(ry)) 3533 return 0; 3534 3535 return cast(bool)(fabs(rx - ry) <= precision); 3536 } 3537 3538 // Make given typed literal 3539 F Literal(F)(F f) 3540 { 3541 return f; 3542 } 3543 3544 static foreach (Float; AliasSeq!(float, double, real)) 3545 { 3546 assert(to!Float("123") == Literal!Float(123)); 3547 assert(to!Float("+123") == Literal!Float(+123)); 3548 assert(to!Float("-123") == Literal!Float(-123)); 3549 assert(to!Float("123e2") == Literal!Float(123e2)); 3550 assert(to!Float("123e+2") == Literal!Float(123e+2)); 3551 assert(to!Float("123e-2") == Literal!Float(123e-2L)); 3552 assert(to!Float("123.") == Literal!Float(123.0)); 3553 assert(to!Float(".375") == Literal!Float(.375)); 3554 3555 assert(to!Float("1.23375E+2") == Literal!Float(1.23375E+2)); 3556 3557 assert(to!Float("0") is 0.0); 3558 assert(to!Float("-0") is -0.0); 3559 3560 assert(isNaN(to!Float("nan"))); 3561 3562 assertThrown!ConvException(to!Float("\x00")); 3563 } 3564 3565 // min and max 3566 float f = to!float("1.17549e-38"); 3567 assert(feq(cast(real) f, cast(real) 1.17549e-38)); 3568 assert(feq(cast(real) f, cast(real) float.min_normal)); 3569 f = to!float("3.40282e+38"); 3570 assert(to!string(f) == to!string(3.40282e+38)); 3571 3572 // min and max 3573 double d = to!double("2.22508e-308"); 3574 assert(feq(cast(real) d, cast(real) 2.22508e-308)); 3575 assert(feq(cast(real) d, cast(real) double.min_normal)); 3576 d = to!double("1.79769e+308"); 3577 assert(to!string(d) == to!string(1.79769e+308)); 3578 assert(to!string(d) == to!string(double.max)); 3579 3580 auto z = real.max / 2L; 3581 static assert(is(typeof(z) == real)); 3582 assert(!isNaN(z)); 3583 assert(!isInfinity(z)); 3584 string a = to!string(z); 3585 real b = to!real(a); 3586 string c = to!string(b); 3587 3588 assert(c == a, "\n" ~ c ~ "\n" ~ a); 3589 3590 assert(to!string(to!real(to!string(real.max / 2L))) == to!string(real.max / 2L)); 3591 3592 // min and max 3593 real r = to!real(to!string(real.min_normal)); 3594 version (NetBSD) 3595 { 3596 // NetBSD notice 3597 // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value 3598 // Simple C code 3599 // long double rd = 3.3621e-4932L; 3600 // printf("%Le\n", rd); 3601 // has unexpected result: 1.681050e-4932 3602 // 3603 // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937 3604 } 3605 else 3606 { 3607 assert(to!string(r) == to!string(real.min_normal)); 3608 } 3609 r = to!real(to!string(real.max)); 3610 assert(to!string(r) == to!string(real.max)); 3611 3612 real pi = 3.1415926535897932384626433832795028841971693993751L; 3613 string fullPrecision = "3.1415926535897932384626433832795028841971693993751"; 3614 assert(feq(parse!real(fullPrecision), pi, 2*real.epsilon)); 3615 string fullPrecision2 = "3.1415926535897932384626433832795028841971693993751"; 3616 assert(feq(parse!(real, string, No.doCount)(fullPrecision2), pi, 2*real.epsilon)); 3617 string fullPrecision3= "3.1415926535897932384626433832795028841971693993751"; 3618 auto len = fullPrecision3.length; 3619 auto res = parse!(real, string, Yes.doCount)(fullPrecision3); 3620 assert(feq(res.data, pi, 2*real.epsilon)); 3621 assert(res.count == len); 3622 3623 real x = 0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252L; 3624 string full = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; 3625 assert(parse!real(full) == x); 3626 string full2 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; 3627 assert(parse!(real, string, No.doCount)(full2) == x); 3628 string full3 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; 3629 auto len2 = full3.length; 3630 assert(parse!(real, string, Yes.doCount)(full3) == tuple(x, len2)); 3631 } 3632 3633 // Tests for the double implementation 3634 @system unittest 3635 { 3636 // @system because strtod is not @safe. 3637 import std.math.traits : floatTraits, RealFormat; 3638 3639 static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 3640 { 3641 import core.stdc.stdlib, std.exception, std.math; 3642 3643 //Should be parsed exactly: 53 bit mantissa 3644 string s = "0x1A_BCDE_F012_3456p10"; 3645 auto x = parse!real(s); 3646 assert(x == 0x1A_BCDE_F012_3456p10L); 3647 //1 bit is implicit 3648 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0xA_BCDE_F012_3456); 3649 assert(strtod("0x1ABCDEF0123456p10", null) == x); 3650 3651 s = "0x1A_BCDE_F012_3456p10"; 3652 auto len = s.length; 3653 assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len)); 3654 3655 //Should be parsed exactly: 10 bit mantissa 3656 s = "0x3FFp10"; 3657 x = parse!real(s); 3658 assert(x == 0x03FFp10); 3659 //1 bit is implicit 3660 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_F800_0000_0000); 3661 assert(strtod("0x3FFp10", null) == x); 3662 3663 //60 bit mantissa, round up 3664 s = "0xFFF_FFFF_FFFF_FFFFp10"; 3665 x = parse!real(s); 3666 assert(isClose(x, 0xFFF_FFFF_FFFF_FFFFp10)); 3667 //1 bit is implicit 3668 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0000); 3669 assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x); 3670 3671 //60 bit mantissa, round down 3672 s = "0xFFF_FFFF_FFFF_FF90p10"; 3673 x = parse!real(s); 3674 assert(isClose(x, 0xFFF_FFFF_FFFF_FF90p10)); 3675 //1 bit is implicit 3676 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_FFFF_FFFF_FFFF); 3677 assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x); 3678 3679 //61 bit mantissa, round up 2 3680 s = "0x1F0F_FFFF_FFFF_FFFFp10"; 3681 x = parse!real(s); 3682 assert(isClose(x, 0x1F0F_FFFF_FFFF_FFFFp10)); 3683 //1 bit is implicit 3684 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_1000_0000_0000); 3685 assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x); 3686 3687 //61 bit mantissa, round down 2 3688 s = "0x1F0F_FFFF_FFFF_FF10p10"; 3689 x = parse!real(s); 3690 assert(isClose(x, 0x1F0F_FFFF_FFFF_FF10p10)); 3691 //1 bit is implicit 3692 assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_0FFF_FFFF_FFFF); 3693 assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x); 3694 3695 //Huge exponent 3696 s = "0x1F_FFFF_FFFF_FFFFp900"; 3697 x = parse!real(s); 3698 assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x); 3699 3700 //exponent too big -> converror 3701 s = ""; 3702 assertThrown!ConvException(x = parse!real(s)); 3703 assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity); 3704 3705 //-exponent too big -> 0 3706 s = "0x1FFFFFFFFFFFFFp-2000"; 3707 x = parse!real(s); 3708 assert(x == 0); 3709 assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x); 3710 3711 s = "0x1FFFFFFFFFFFFFp-2000"; 3712 len = s.length; 3713 assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len)); 3714 } 3715 } 3716 3717 @system unittest 3718 { 3719 import core.stdc.errno; 3720 import core.stdc.stdlib; 3721 import std.math.traits : floatTraits, RealFormat; 3722 3723 errno = 0; // In case it was set by another unittest in a different module. 3724 struct longdouble 3725 { 3726 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) 3727 { 3728 ushort[8] value; 3729 } 3730 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended || 3731 floatTraits!real.realFormat == RealFormat.ieeeExtended53) 3732 { 3733 ushort[5] value; 3734 } 3735 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 3736 { 3737 ushort[4] value; 3738 } 3739 else 3740 static assert(false, "Not implemented"); 3741 } 3742 3743 real ld; 3744 longdouble x; 3745 real ld1; 3746 longdouble x1; 3747 int i; 3748 3749 static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) 3750 enum s = "0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382"; 3751 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 3752 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; 3753 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53) 3754 enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; 3755 else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) 3756 enum s = "0x1.FFFFFFFFFFFFFFFEp-1000"; 3757 else 3758 static assert(false, "Floating point format for real not supported"); 3759 3760 auto s2 = s.idup; 3761 ld = parse!real(s2); 3762 assert(s2.empty); 3763 x = *cast(longdouble *)&ld; 3764 3765 static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) 3766 { 3767 version (CRuntime_Microsoft) 3768 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod 3769 else 3770 ld1 = strtold(s.ptr, null); 3771 } 3772 else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53) 3773 ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold rounds to 53 bits. 3774 else 3775 ld1 = strtold(s.ptr, null); 3776 3777 x1 = *cast(longdouble *)&ld1; 3778 assert(x1 == x && ld1 == ld); 3779 3780 assert(!errno); 3781 3782 s2 = "1.0e5"; 3783 ld = parse!real(s2); 3784 assert(s2.empty); 3785 x = *cast(longdouble *)&ld; 3786 ld1 = strtold("1.0e5", null); 3787 x1 = *cast(longdouble *)&ld1; 3788 } 3789 3790 @safe pure unittest 3791 { 3792 import std.exception; 3793 3794 // https://issues.dlang.org/show_bug.cgi?id=4959 3795 { 3796 auto s = "0 "; 3797 auto x = parse!double(s); 3798 assert(s == " "); 3799 assert(x == 0.0); 3800 } 3801 { 3802 auto s = "0 "; 3803 auto x = parse!(double, string, Yes.doCount)(s); 3804 assert(s == " "); 3805 assert(x == tuple(0.0, 1)); 3806 } 3807 3808 // https://issues.dlang.org/show_bug.cgi?id=3369 3809 assert(to!float("inf") == float.infinity); 3810 assert(to!float("-inf") == -float.infinity); 3811 3812 // https://issues.dlang.org/show_bug.cgi?id=6160 3813 assert(6_5.536e3L == to!real("6_5.536e3")); // 2^16 3814 assert(0x1000_000_000_p10 == to!real("0x1000_000_000_p10")); // 7.03687e+13 3815 3816 // https://issues.dlang.org/show_bug.cgi?id=6258 3817 assertThrown!ConvException(to!real("-")); 3818 assertThrown!ConvException(to!real("in")); 3819 3820 // https://issues.dlang.org/show_bug.cgi?id=7055 3821 assertThrown!ConvException(to!float("INF2")); 3822 3823 //extra stress testing 3824 auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", "3.4_", 3825 "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2", 3826 "nan", "-NAN", "+NaN", "-nAna", "NAn2e2", "-naN2e2"]; 3827 auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", 3828 "+inf", "-in", "I", "+N", "-NaD", "0x3.F"]; 3829 foreach (s; ssOK) 3830 parse!double(s); 3831 foreach (s; ssKO) 3832 assertThrown!ConvException(parse!double(s)); 3833 } 3834 3835 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=22637 3836 { 3837 import std.exception : assertThrown, assertNotThrown; 3838 auto src = "9991232549867999698999493543521458974414359998784641646846435132132543645435456345634541999999999999999" 3839 ~ "9999999943321231321311999231345312413646846354354354399999934153465464654646464654134135354199999999996515734999" 3840 ~ "9999999320135273486741354354731567431324134999999999999999999999999999999999999999999999135411.9"; 3841 assertThrown!ConvException(parse!double(src)); 3842 static if (real.max_10_exp > 310) assertNotThrown!ConvException(parse!real(src)); 3843 } 3844 3845 /** 3846 Parses one character from a character range. 3847 3848 Params: 3849 Target = the type to convert to 3850 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 3851 doCount = the flag for deciding to report the number of consumed characters 3852 3853 Returns: 3854 $(UL 3855 $(LI A character of type `Target` if `doCount` is set to `No.doCount`) 3856 $(LI A `tuple` containing a character of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 3857 3858 Throws: 3859 A $(LREF ConvException) if the range is empty. 3860 */ 3861 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 3862 if (staticIndexOf!(immutable Target, immutable dchar, immutable ElementEncodingType!Source) >= 0 && 3863 isSomeString!Source && !is(Source == enum)) 3864 { 3865 if (s.empty) 3866 throw convError!(Source, Target)(s); 3867 static if (is(immutable Target == immutable dchar)) 3868 { 3869 Target result = s.front; 3870 s.popFront(); 3871 static if (doCount) 3872 { 3873 return tuple!("data", "count")(result, 1); 3874 } 3875 else 3876 { 3877 return result; 3878 } 3879 3880 } 3881 else 3882 { 3883 // Special case: okay so parse a Char off a Char[] 3884 Target result = s[0]; 3885 s = s[1 .. $]; 3886 static if (doCount) 3887 { 3888 return tuple!("data", "count")(result, 1); 3889 } 3890 else 3891 { 3892 return result; 3893 } 3894 } 3895 } 3896 3897 @safe pure unittest 3898 { 3899 static foreach (Str; AliasSeq!(string, wstring, dstring)) 3900 { 3901 static foreach (Char; AliasSeq!(char, wchar, dchar)) 3902 {{ 3903 static if (is(immutable Char == immutable dchar) || 3904 Char.sizeof == ElementEncodingType!Str.sizeof) 3905 { 3906 Str s = "aaa"; 3907 assert(parse!Char(s) == 'a'); 3908 assert(s == "aa"); 3909 assert(parse!(Char, typeof(s), No.doCount)(s) == 'a'); 3910 assert(s == "a"); 3911 assert(parse!(Char, typeof(s), Yes.doCount)(s) == tuple('a', 1) && s == ""); 3912 } 3913 }} 3914 } 3915 } 3916 3917 /// ditto 3918 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 3919 if (isSomeChar!Target && Target.sizeof >= ElementType!Source.sizeof && !is(Target == enum) && 3920 !isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Source)) 3921 { 3922 if (s.empty) 3923 throw convError!(Source, Target)(s); 3924 Target result = s.front; 3925 s.popFront(); 3926 static if (doCount) 3927 { 3928 return tuple!("data", "count")(result, 1); 3929 } 3930 else 3931 { 3932 return result; 3933 } 3934 } 3935 3936 /// 3937 @safe pure unittest 3938 { 3939 import std.typecons : Flag, Yes, No; 3940 auto s = "Hello, World!"; 3941 char first = parse!char(s); 3942 assert(first == 'H'); 3943 assert(s == "ello, World!"); 3944 char second = parse!(char, string, No.doCount)(s); 3945 assert(second == 'e'); 3946 assert(s == "llo, World!"); 3947 auto third = parse!(char, string, Yes.doCount)(s); 3948 assert(third.data == 'l' && third.count == 1); 3949 assert(s == "lo, World!"); 3950 } 3951 3952 3953 /* 3954 Tests for to!bool and parse!bool 3955 */ 3956 @safe pure unittest 3957 { 3958 import std.exception; 3959 3960 assert(to!bool("TruE") == true); 3961 assert(to!bool("faLse"d) == false); 3962 assertThrown!ConvException(to!bool("maybe")); 3963 3964 auto t = "TrueType"; 3965 assert(parse!bool(t) == true); 3966 assert(t == "Type"); 3967 3968 auto f = "False killer whale"d; 3969 assert(parse!bool(f) == false); 3970 assert(f == " killer whale"d); 3971 3972 f = "False killer whale"d; 3973 assert(parse!(bool, dstring, Yes.doCount)(f) == tuple(false, 5)); 3974 assert(f == " killer whale"d); 3975 3976 auto m = "maybe"; 3977 assertThrown!ConvException(parse!bool(m)); 3978 assertThrown!ConvException(parse!(bool, string, Yes.doCount)(m)); 3979 assert(m == "maybe"); // m shouldn't change on failure 3980 3981 auto s = "true"; 3982 auto b = parse!(const(bool))(s); 3983 assert(b == true); 3984 } 3985 3986 /** 3987 Parses `typeof(null)` from a character range if the range 3988 spells `"null"`. This function is case insensitive. 3989 3990 Params: 3991 Target = the type to convert to 3992 s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 3993 doCount = the flag for deciding to report the number of consumed characters 3994 3995 Returns: 3996 $(UL 3997 $(LI `null` if `doCount` is set to `No.doCount`) 3998 $(LI A `tuple` containing `null` and a `size_t` if `doCount` is set to `Yes.doCount`)) 3999 4000 Throws: 4001 A $(LREF ConvException) if the range doesn't represent `null`. 4002 */ 4003 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4004 if (is(immutable Target == immutable typeof(null)) && 4005 isInputRange!Source && 4006 isSomeChar!(ElementType!Source)) 4007 { 4008 import std.ascii : toLower; 4009 foreach (c; "null") 4010 { 4011 if (s.empty || toLower(s.front) != c) 4012 throw parseError("null should be case-insensitive 'null'"); 4013 s.popFront(); 4014 } 4015 static if (doCount) 4016 { 4017 return tuple!("data", "count")(null, 4); 4018 } 4019 else 4020 { 4021 return null; 4022 } 4023 } 4024 4025 /// 4026 @safe pure unittest 4027 { 4028 import std.exception : assertThrown; 4029 import std.typecons : Flag, Yes, No; 4030 4031 alias NullType = typeof(null); 4032 auto s1 = "null"; 4033 assert(parse!NullType(s1) is null); 4034 assert(s1 == ""); 4035 4036 auto s2 = "NUll"d; 4037 assert(parse!NullType(s2) is null); 4038 assert(s2 == ""); 4039 4040 auto s3 = "nuLlNULl"; 4041 assert(parse!(NullType, string, No.doCount)(s3) is null); 4042 auto r = parse!(NullType, string, Yes.doCount)(s3); 4043 assert(r.data is null && r.count == 4); 4044 4045 auto m = "maybe"; 4046 assertThrown!ConvException(parse!NullType(m)); 4047 assertThrown!ConvException(parse!(NullType, string, Yes.doCount)(m)); 4048 assert(m == "maybe"); // m shouldn't change on failure 4049 4050 auto s = "NULL"; 4051 assert(parse!(const NullType)(s) is null); 4052 } 4053 4054 //Used internally by parse Array/AA, to remove ascii whites 4055 package auto skipWS(R, Flag!"doCount" doCount = No.doCount)(ref R r) 4056 { 4057 import std.ascii : isWhite; 4058 static if (isSomeString!R) 4059 { 4060 //Implementation inspired from stripLeft. 4061 foreach (i, c; r) 4062 { 4063 if (!isWhite(c)) 4064 { 4065 r = r[i .. $]; 4066 static if (doCount) 4067 { 4068 return i; 4069 } 4070 else 4071 { 4072 return; 4073 } 4074 } 4075 } 4076 auto len = r.length; 4077 r = r[0 .. 0]; //Empty string with correct type. 4078 static if (doCount) 4079 { 4080 return len; 4081 } 4082 else 4083 { 4084 return; 4085 } 4086 } 4087 else 4088 { 4089 size_t i = 0; 4090 for (; !r.empty && isWhite(r.front); r.popFront(), ++i) 4091 { } 4092 static if (doCount) 4093 { 4094 return i; 4095 } 4096 } 4097 } 4098 4099 /** 4100 * Parses an array from a string given the left bracket (default $(D 4101 * '[')), right bracket (default `']'`), and element separator (by 4102 * default `','`). A trailing separator is allowed. 4103 * 4104 * Params: 4105 * s = The string to parse 4106 * lbracket = the character that starts the array 4107 * rbracket = the character that ends the array 4108 * comma = the character that separates the elements of the array 4109 * doCount = the flag for deciding to report the number of consumed characters 4110 * 4111 * Returns: 4112 $(UL 4113 * $(LI An array of type `Target` if `doCount` is set to `No.doCount`) 4114 * $(LI A `tuple` containing an array of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`)) 4115 */ 4116 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', 4117 dchar rbracket = ']', dchar comma = ',') 4118 if (isDynamicArray!Target && !is(Target == enum) && 4119 isSomeString!Source && !is(Source == enum)) 4120 { 4121 import std.array : appender; 4122 4123 auto result = appender!Target(); 4124 4125 parseCheck!s(lbracket); 4126 static if (doCount) 4127 size_t count = 1; 4128 static if (doCount) 4129 count += skipWS!(Source, Yes.doCount)(s); 4130 else 4131 skipWS!(Source, No.doCount)(s); 4132 if (s.empty) 4133 throw convError!(Source, Target)(s); 4134 if (s.front == rbracket) 4135 { 4136 s.popFront(); 4137 static if (doCount) 4138 { 4139 ++count; 4140 return tuple!("data", "count")(result.data, count); 4141 } 4142 else 4143 { 4144 return result.data; 4145 } 4146 } 4147 for (;;) 4148 { 4149 if (!s.empty && s.front == rbracket) 4150 break; 4151 static if (doCount) 4152 { 4153 auto r = parseElement!(WideElementType!Target, Source, Yes.doCount)(s); 4154 result ~= r.data; 4155 count += r.count; 4156 count += skipWS!(Source, Yes.doCount)(s); 4157 } 4158 else 4159 { 4160 auto r = parseElement!(WideElementType!Target, Source, No.doCount)(s); 4161 result ~= r; 4162 skipWS!(Source, No.doCount)(s); 4163 } 4164 if (s.empty) 4165 throw convError!(Source, Target)(s); 4166 if (s.front != comma) 4167 break; 4168 s.popFront(); 4169 static if (doCount) 4170 ++count; 4171 static if (doCount) 4172 count += skipWS!(Source, Yes.doCount)(s); 4173 else 4174 skipWS!(Source, No.doCount)(s); 4175 } 4176 parseCheck!s(rbracket); 4177 static if (doCount) 4178 { 4179 ++count; 4180 return tuple!("data", "count")(result.data, count); 4181 } 4182 else 4183 { 4184 return result.data; 4185 } 4186 } 4187 /// 4188 @safe pure unittest 4189 { 4190 import std.typecons : Flag, Yes, No; 4191 auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; 4192 auto a1 = parse!(string[])(s1); 4193 assert(a1 == ["hello", "world"]); 4194 4195 auto s2 = `["aaa", "bbb", "ccc"]`; 4196 auto a2 = parse!(string[])(s2); 4197 assert(a2 == ["aaa", "bbb", "ccc"]); 4198 4199 auto s3 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; 4200 auto len3 = s3.length; 4201 auto a3 = parse!(string[], string, Yes.doCount)(s3); 4202 assert(a3.data == ["hello", "world"]); 4203 assert(a3.count == len3); 4204 } 4205 4206 // https://issues.dlang.org/show_bug.cgi?id=9615 4207 @safe unittest 4208 { 4209 import std.typecons : Flag, Yes, No, tuple; 4210 string s0 = "[1,2, ]"; 4211 string s1 = "[1,2, \t\v\r\n]"; 4212 string s2 = "[1,2]"; 4213 assert(s0.parse!(int[]) == [1,2]); 4214 assert(s1.parse!(int[]) == [1,2]); 4215 assert(s2.parse!(int[]) == [1,2]); 4216 4217 s0 = "[1,2, ]"; 4218 auto len0 = s0.length; 4219 s1 = "[1,2, \t\v\r\n]"; 4220 auto len1 = s1.length; 4221 s2 = "[1,2]"; 4222 auto len2 = s2.length; 4223 assert(s0.parse!(int[], string, Yes.doCount) == tuple([1,2], len0)); 4224 assert(s1.parse!(int[], string, Yes.doCount) == tuple([1,2], len1)); 4225 assert(s2.parse!(int[], string, Yes.doCount) == tuple([1,2], len2)); 4226 4227 string s3 = `["a","b",]`; 4228 string s4 = `["a","b"]`; 4229 assert(s3.parse!(string[]) == ["a","b"]); 4230 assert(s4.parse!(string[]) == ["a","b"]); 4231 4232 s3 = `["a","b",]`; 4233 auto len3 = s3.length; 4234 assert(s3.parse!(string[], string, Yes.doCount) == tuple(["a","b"], len3)); 4235 4236 s3 = `[ ]`; 4237 assert(tuple([], s3.length) == s3.parse!(string[], string, Yes.doCount)); 4238 4239 import std.exception : assertThrown; 4240 string s5 = "[,]"; 4241 string s6 = "[, \t,]"; 4242 assertThrown!ConvException(parse!(string[])(s5)); 4243 assertThrown!ConvException(parse!(int[])(s6)); 4244 4245 s5 = "[,]"; 4246 s6 = "[,·\t,]"; 4247 assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s5)); 4248 assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s6)); 4249 } 4250 4251 @safe unittest 4252 { 4253 int[] a = [1, 2, 3, 4, 5]; 4254 auto s = to!string(a); 4255 assert(to!(int[])(s) == a); 4256 } 4257 4258 @safe unittest 4259 { 4260 int[][] a = [ [1, 2] , [3], [4, 5] ]; 4261 auto s = to!string(a); 4262 assert(to!(int[][])(s) == a); 4263 } 4264 4265 @safe unittest 4266 { 4267 int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ]; 4268 4269 char[] s = to!(char[])(ia); 4270 int[][][] ia2; 4271 4272 ia2 = to!(typeof(ia2))(s); 4273 assert( ia == ia2); 4274 } 4275 4276 @safe pure unittest 4277 { 4278 import std.exception; 4279 import std.typecons : Flag, Yes, No; 4280 4281 //Check proper failure 4282 auto s = "[ 1 , 2 , 3 ]"; 4283 auto s2 = s.save; 4284 foreach (i ; 0 .. s.length-1) 4285 { 4286 auto ss = s[0 .. i]; 4287 assertThrown!ConvException(parse!(int[])(ss)); 4288 assertThrown!ConvException(parse!(int[], string, Yes.doCount)(ss)); 4289 } 4290 int[] arr = parse!(int[])(s); 4291 auto arr2 = parse!(int[], string, Yes.doCount)(s2); 4292 arr = arr2.data; 4293 } 4294 4295 @safe pure unittest 4296 { 4297 //Checks parsing of strings with escaped characters 4298 string s1 = `[ 4299 "Contains a\0null!", 4300 "tab\there", 4301 "line\nbreak", 4302 "backslash \\ slash / question \?", 4303 "number \x35 five", 4304 "unicode \u65E5 sun", 4305 "very long \U000065E5 sun" 4306 ]`; 4307 4308 //Note: escaped characters purposefully replaced and isolated to guarantee 4309 //there are no typos in the escape syntax 4310 string[] s2 = [ 4311 "Contains a" ~ '\0' ~ "null!", 4312 "tab" ~ '\t' ~ "here", 4313 "line" ~ '\n' ~ "break", 4314 "backslash " ~ '\\' ~ " slash / question ?", 4315 "number 5 five", 4316 "unicode 日 sun", 4317 "very long 日 sun" 4318 ]; 4319 string s3 = s1.save; 4320 assert(s2 == parse!(string[])(s1)); 4321 assert(s1.empty); 4322 assert(tuple(s2, s3.length) == parse!(string[], string, Yes.doCount)(s3)); 4323 } 4324 4325 /// ditto 4326 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', 4327 dchar rbracket = ']', dchar comma = ',') 4328 if (isStaticArray!Target && !is(Target == enum) && 4329 isExactSomeString!Source) 4330 { 4331 static if (hasIndirections!Target) 4332 Target result = Target.init[0].init; 4333 else 4334 Target result = void; 4335 4336 parseCheck!s(lbracket); 4337 static if (doCount) 4338 size_t count = 1; 4339 static if (doCount) 4340 count += skipWS!(Source, Yes.doCount)(s); 4341 else 4342 skipWS!(Source, No.doCount)(s); 4343 if (s.empty) 4344 throw convError!(Source, Target)(s); 4345 if (s.front == rbracket) 4346 { 4347 static if (result.length != 0) 4348 goto Lmanyerr; 4349 else 4350 { 4351 s.popFront(); 4352 static if (doCount) 4353 { 4354 ++count; 4355 return tuple!("data", "count")(result, count); 4356 } 4357 else 4358 { 4359 return result; 4360 } 4361 } 4362 } 4363 for (size_t i = 0; ; ) 4364 { 4365 if (i == result.length) 4366 goto Lmanyerr; 4367 static if (doCount) 4368 { 4369 auto r = parseElement!(ElementType!Target, Source, Yes.doCount)(s); 4370 result[i++] = r.data; 4371 count += r.count; 4372 count += skipWS!(Source, Yes.doCount)(s); 4373 } 4374 else 4375 { 4376 auto r = parseElement!(ElementType!Target, Source, No.doCount)(s); 4377 result[i++] = r; 4378 skipWS!(Source, No.doCount)(s); 4379 } 4380 if (s.empty) 4381 throw convError!(Source, Target)(s); 4382 if (s.front != comma) 4383 { 4384 if (i != result.length) 4385 goto Lfewerr; 4386 break; 4387 } 4388 s.popFront(); 4389 static if (doCount) 4390 ++count; 4391 static if (doCount) 4392 count += skipWS!(Source, Yes.doCount)(s); 4393 else 4394 skipWS!(Source, No.doCount)(s); 4395 } 4396 parseCheck!s(rbracket); 4397 static if (doCount) 4398 { 4399 ++count; 4400 return tuple!("data", "count")(result, count); 4401 } 4402 else 4403 { 4404 return result; 4405 } 4406 4407 Lmanyerr: 4408 throw parseError(text("Too many elements in input, ", result.length, " elements expected.")); 4409 Lfewerr: 4410 throw parseError(text("Too few elements in input, ", result.length, " elements expected.")); 4411 } 4412 4413 @safe pure unittest 4414 { 4415 import std.exception; 4416 4417 auto s1 = "[1,2,3,4]"; 4418 auto sa1 = parse!(int[4])(s1); 4419 assert(sa1 == [1,2,3,4]); 4420 s1 = "[1,2,3,4]"; 4421 assert(tuple([1,2,3,4], s1.length) == parse!(int[4], string, Yes.doCount)(s1)); 4422 4423 auto s2 = "[[1],[2,3],[4]]"; 4424 auto sa2 = parse!(int[][3])(s2); 4425 assert(sa2 == [[1],[2,3],[4]]); 4426 s2 = "[[1],[2,3],[4]]"; 4427 assert(tuple([[1],[2,3],[4]], s2.length) == parse!(int[][3], string, Yes.doCount)(s2)); 4428 4429 auto s3 = "[1,2,3]"; 4430 assertThrown!ConvException(parse!(int[4])(s3)); 4431 assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s3)); 4432 4433 auto s4 = "[1,2,3,4,5]"; 4434 assertThrown!ConvException(parse!(int[4])(s4)); 4435 assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s4)); 4436 } 4437 4438 /** 4439 * Parses an associative array from a string given the left bracket (default $(D 4440 * '[')), right bracket (default `']'`), key-value separator (default $(D 4441 * ':')), and element seprator (by default `','`). 4442 * 4443 * Params: 4444 * s = the string to parse 4445 * lbracket = the character that starts the associative array 4446 * rbracket = the character that ends the associative array 4447 * keyval = the character that associates the key with the value 4448 * comma = the character that separates the elements of the associative array 4449 * doCount = the flag for deciding to report the number of consumed characters 4450 * 4451 * Returns: 4452 $(UL 4453 * $(LI An associative array of type `Target` if `doCount` is set to `No.doCount`) 4454 * $(LI A `tuple` containing an associative array of type `Target` and a `size_t` 4455 * if `doCount` is set to `Yes.doCount`)) 4456 */ 4457 auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[', 4458 dchar rbracket = ']', dchar keyval = ':', dchar comma = ',') 4459 if (isAssociativeArray!Target && !is(Target == enum) && 4460 isSomeString!Source && !is(Source == enum)) 4461 { 4462 alias KeyType = typeof(Target.init.keys[0]); 4463 alias ValType = typeof(Target.init.values[0]); 4464 4465 Target result; 4466 static if (doCount) size_t count; 4467 4468 parseCheck!s(lbracket); 4469 static if (doCount) 4470 count = 1 + skipWS!(Source, Yes.doCount)(s); 4471 else 4472 skipWS!(Source, No.doCount)(s); 4473 if (s.empty) 4474 throw convError!(Source, Target)(s); 4475 if (s.front == rbracket) 4476 { 4477 s.popFront(); 4478 static if (doCount) 4479 return tuple!("data", "count")(result, count + 1); 4480 else 4481 return result; 4482 } 4483 for (;;) 4484 { 4485 static if (doCount) 4486 { 4487 auto key = parseElement!(KeyType, Source, Yes.doCount)(s); 4488 count += key.count + skipWS!(Source, Yes.doCount)(s); 4489 parseCheck!s(keyval); 4490 count += 1 + skipWS!(Source, Yes.doCount)(s); 4491 auto val = parseElement!(ValType, Source, Yes.doCount)(s); 4492 count += val.count + skipWS!(Source, Yes.doCount)(s); 4493 result[key.data] = val.data; 4494 } 4495 else 4496 { 4497 auto key = parseElement!(KeyType, Source, No.doCount)(s); 4498 skipWS!(Source, No.doCount)(s); 4499 parseCheck!s(keyval); 4500 skipWS!(Source, No.doCount)(s); 4501 auto val = parseElement!(ValType, Source, No.doCount)(s); 4502 skipWS!(Source, No.doCount)(s); 4503 result[key] = val; 4504 } 4505 if (s.empty) 4506 throw convError!(Source, Target)(s); 4507 if (s.front != comma) 4508 break; 4509 s.popFront(); 4510 static if (doCount) 4511 count += 1 + skipWS!(Source, Yes.doCount)(s); 4512 else 4513 skipWS!(Source, No.doCount)(s); 4514 } 4515 parseCheck!s(rbracket); 4516 static if (doCount) 4517 return tuple!("data", "count")(result, count + 1); 4518 else 4519 return result; 4520 } 4521 4522 /// 4523 @safe pure unittest 4524 { 4525 import std.typecons : Flag, Yes, No, tuple; 4526 import std.range.primitives : save; 4527 import std.array : assocArray; 4528 auto s1 = "[1:10, 2:20, 3:30]"; 4529 auto copyS1 = s1.save; 4530 auto aa1 = parse!(int[int])(s1); 4531 assert(aa1 == [1:10, 2:20, 3:30]); 4532 assert(tuple([1:10, 2:20, 3:30], copyS1.length) == parse!(int[int], string, Yes.doCount)(copyS1)); 4533 4534 auto s2 = `["aaa":10, "bbb":20, "ccc":30]`; 4535 auto copyS2 = s2.save; 4536 auto aa2 = parse!(int[string])(s2); 4537 assert(aa2 == ["aaa":10, "bbb":20, "ccc":30]); 4538 assert(tuple(["aaa":10, "bbb":20, "ccc":30], copyS2.length) == 4539 parse!(int[string], string, Yes.doCount)(copyS2)); 4540 4541 auto s3 = `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`; 4542 auto copyS3 = s3.save; 4543 auto aa3 = parse!(int[][string])(s3); 4544 assert(aa3 == ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]); 4545 assert(tuple(["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]], copyS3.length) == 4546 parse!(int[][string], string, Yes.doCount)(copyS3)); 4547 4548 auto s4 = `[]`; 4549 int[int] emptyAA; 4550 assert(tuple(emptyAA, s4.length) == parse!(int[int], string, Yes.doCount)(s4)); 4551 } 4552 4553 @safe pure unittest 4554 { 4555 import std.exception; 4556 4557 //Check proper failure 4558 auto s = "[1:10, 2:20, 3:30]"; 4559 auto s2 = s.save; 4560 foreach (i ; 0 .. s.length-1) 4561 { 4562 auto ss = s[0 .. i]; 4563 assertThrown!ConvException(parse!(int[int])(ss)); 4564 assertThrown!ConvException(parse!(int[int], string, Yes.doCount)(ss)); 4565 } 4566 int[int] aa = parse!(int[int])(s); 4567 auto aa2 = parse!(int[int], string, Yes.doCount)(s2); 4568 aa = aa2[0]; 4569 4570 } 4571 4572 private auto parseEscape(Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4573 if (isInputRange!Source && isSomeChar!(ElementType!Source)) 4574 { 4575 parseCheck!s('\\'); 4576 size_t count; 4577 static if (doCount) 4578 count = 1; 4579 if (s.empty) 4580 throw parseError("Unterminated escape sequence"); 4581 4582 // consumes 1 element from Source 4583 dchar getHexDigit()(ref Source s_ = s) // workaround 4584 { 4585 import std.ascii : isAlpha, isHexDigit; 4586 if (s_.empty) 4587 throw parseError("Unterminated escape sequence"); 4588 s_.popFront(); 4589 if (s_.empty) 4590 throw parseError("Unterminated escape sequence"); 4591 dchar c = s_.front; 4592 if (!isHexDigit(c)) 4593 throw parseError("Hex digit is missing"); 4594 return isAlpha(c) ? ((c & ~0x20) - ('A' - 10)) : c - '0'; 4595 } 4596 // We need to do octals separate, because they need a lookahead to find out, 4597 // where the escape sequence ends. 4598 auto first = s.front; 4599 if (first >= '0' && first <= '7') 4600 { 4601 dchar c1 = s.front; 4602 s.popFront(); 4603 static if (doCount) 4604 ++count; 4605 if (s.empty) 4606 { 4607 static if (doCount) 4608 { 4609 return tuple!("data", "count")(cast (dchar) (c1 - '0'), count); 4610 } 4611 else 4612 { 4613 return cast (dchar) (c1 - '0'); 4614 } 4615 } 4616 dchar c2 = s.front; 4617 if (c2 < '0' || c2 > '7') 4618 { 4619 static if (doCount) 4620 { 4621 return tuple!("data", "count")(cast (dchar)(c1 - '0'), count); 4622 } 4623 else 4624 { 4625 return cast (dchar)(c1 - '0'); 4626 } 4627 } 4628 s.popFront(); 4629 static if (doCount) 4630 ++count; 4631 dchar c3 = s.front; 4632 if (c3 < '0' || c3 > '7') 4633 { 4634 static if (doCount) 4635 { 4636 return tuple!("data", "count")(cast (dchar) (8 * (c1 - '0') + (c2 - '0')), count); 4637 } 4638 else 4639 { 4640 return cast (dchar) (8 * (c1 - '0') + (c2 - '0')); 4641 } 4642 } 4643 s.popFront(); 4644 static if (doCount) 4645 ++count; 4646 if (c1 > '3') 4647 throw parseError("Octal sequence is larger than \\377"); 4648 static if (doCount) 4649 { 4650 return tuple!("data", "count")(cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')), count); 4651 } 4652 else 4653 { 4654 return cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')); 4655 } 4656 } 4657 4658 dchar result; 4659 4660 switch (first) 4661 { 4662 case '"': result = '\"'; break; 4663 case '\'': result = '\''; break; 4664 case '?': result = '\?'; break; 4665 case '\\': result = '\\'; break; 4666 case 'a': result = '\a'; break; 4667 case 'b': result = '\b'; break; 4668 case 'f': result = '\f'; break; 4669 case 'n': result = '\n'; break; 4670 case 'r': result = '\r'; break; 4671 case 't': result = '\t'; break; 4672 case 'v': result = '\v'; break; 4673 case 'x': 4674 result = getHexDigit() << 4; 4675 result |= getHexDigit(); 4676 static if (doCount) 4677 count += 2; 4678 break; 4679 case 'u': 4680 result = getHexDigit() << 12; 4681 result |= getHexDigit() << 8; 4682 result |= getHexDigit() << 4; 4683 result |= getHexDigit(); 4684 static if (doCount) 4685 count += 4; 4686 break; 4687 case 'U': 4688 result = getHexDigit() << 28; 4689 result |= getHexDigit() << 24; 4690 result |= getHexDigit() << 20; 4691 result |= getHexDigit() << 16; 4692 result |= getHexDigit() << 12; 4693 result |= getHexDigit() << 8; 4694 result |= getHexDigit() << 4; 4695 result |= getHexDigit(); 4696 static if (doCount) 4697 count += 8; 4698 break; 4699 default: 4700 throw parseError("Unknown escape character " ~ to!string(s.front)); 4701 } 4702 if (s.empty) 4703 throw parseError("Unterminated escape sequence"); 4704 4705 s.popFront(); 4706 4707 static if (doCount) 4708 { 4709 return tuple!("data", "count")(cast (dchar) result, ++count); 4710 } 4711 else 4712 { 4713 return cast (dchar) result; 4714 } 4715 } 4716 4717 @safe pure unittest 4718 { 4719 string[] s1 = [ 4720 `\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes 4721 `\141`, 4722 `\x61`, 4723 `\u65E5`, `\U00012456`, 4724 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities) 4725 //`\&`, `\"`, 4726 ]; 4727 string[] copyS1 = s1 ~ s1[0 .. 0]; 4728 4729 const(dchar)[] s2 = [ 4730 '\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes 4731 '\141', 4732 '\x61', 4733 '\u65E5', '\U00012456', 4734 // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities) 4735 //'\&', '\"', 4736 ]; 4737 foreach (i ; 0 .. s1.length) 4738 { 4739 assert(s2[i] == parseEscape(s1[i])); 4740 assert(s1[i].empty); 4741 4742 assert(tuple(s2[i], copyS1[i].length) == parseEscape!(string, Yes.doCount)(copyS1[i])); 4743 assert(copyS1[i].empty); 4744 } 4745 } 4746 4747 @safe pure unittest 4748 { 4749 import std.exception; 4750 4751 string[] ss = [ 4752 `hello!`, //Not an escape 4753 `\`, //Premature termination 4754 `\/`, //Not an escape 4755 `\gggg`, //Not an escape 4756 `\xzz`, //Not an hex 4757 `\x0`, //Premature hex end 4758 `\XB9`, //Not legal hex syntax 4759 `\u!!`, //Not a unicode hex 4760 `\777`, //Octal is larger than a byte 4761 `\80`, //Wrong digit at beginning of octal 4762 `\u123`, //Premature hex end 4763 `\U123123` //Premature hex end 4764 ]; 4765 foreach (s ; ss) 4766 { 4767 assertThrown!ConvException(parseEscape(s)); 4768 assertThrown!ConvException(parseEscape!(string, Yes.doCount)(s)); 4769 } 4770 } 4771 4772 // Undocumented 4773 auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4774 if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 4775 isExactSomeString!Target) 4776 { 4777 import std.array : appender; 4778 auto result = appender!Target(); 4779 4780 // parse array of chars 4781 if (s.empty) 4782 throw convError!(Source, Target)(s); 4783 if (s.front == '[') 4784 { 4785 return parse!(Target, Source, doCount)(s); 4786 } 4787 4788 parseCheck!s('\"'); 4789 size_t count; 4790 static if (doCount) 4791 count = 1; 4792 if (s.empty) 4793 throw convError!(Source, Target)(s); 4794 if (s.front == '\"') 4795 { 4796 s.popFront(); 4797 static if (doCount) 4798 { 4799 count++; 4800 return tuple!("data", "count")(result.data, count); 4801 } 4802 else 4803 { 4804 return result.data; 4805 } 4806 } 4807 while (true) 4808 { 4809 if (s.empty) 4810 throw parseError("Unterminated quoted string"); 4811 switch (s.front) 4812 { 4813 case '\"': 4814 s.popFront(); 4815 static if (doCount) 4816 { 4817 count++; 4818 return tuple!("data", "count")(result.data, count); 4819 } 4820 else 4821 { 4822 return result.data; 4823 } 4824 case '\\': 4825 static if (doCount) 4826 { 4827 auto r = parseEscape!(typeof(s), Yes.doCount)(s); 4828 result.put(r[0]); 4829 count += r[1]; 4830 } 4831 else 4832 { 4833 auto r = parseEscape!(typeof(s), No.doCount)(s); 4834 result.put(r); 4835 } 4836 break; 4837 default: 4838 result.put(s.front); 4839 static if (doCount) 4840 count++; 4841 s.popFront(); 4842 break; 4843 } 4844 } 4845 assert(false, "Unexpected fallthrough"); 4846 } 4847 4848 // ditto 4849 auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4850 if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 4851 is(CharTypeOf!Target == dchar) && !is(Target == enum)) 4852 { 4853 Unqual!Target c; 4854 4855 parseCheck!s('\''); 4856 size_t count; 4857 static if (doCount) 4858 count = 1; 4859 if (s.empty) 4860 throw convError!(Source, Target)(s); 4861 static if (doCount) 4862 count++; 4863 if (s.front != '\\') 4864 { 4865 c = s.front; 4866 s.popFront(); 4867 } 4868 else 4869 { 4870 static if (doCount) 4871 c = parseEscape!(typeof(s), Yes.doCount)(s).data; 4872 else 4873 c = parseEscape!(typeof(s), No.doCount)(s); 4874 } 4875 parseCheck!s('\''); 4876 static if (doCount) 4877 { 4878 count++; 4879 return tuple!("data", "count")(c, count); 4880 } 4881 else 4882 { 4883 return c; 4884 } 4885 } 4886 4887 // ditto 4888 auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s) 4889 if (isInputRange!Source && isSomeChar!(ElementType!Source) && 4890 !isSomeString!Target && !isSomeChar!Target) 4891 { 4892 return parse!(Target, Source, doCount)(s); 4893 } 4894 4895 // Use this when parsing a type that will ultimately be appended to a 4896 // string. 4897 package template WideElementType(T) 4898 { 4899 alias E = ElementType!T; 4900 static if (isSomeChar!E) 4901 alias WideElementType = dchar; 4902 else 4903 alias WideElementType = E; 4904 } 4905 4906 4907 /*************************************************************** 4908 * Convenience functions for converting one or more arguments 4909 * of any type into _text (the three character widths). 4910 */ 4911 string text(T...)(T args) 4912 if (T.length > 0) { return textImpl!string(args); } 4913 4914 ///ditto 4915 wstring wtext(T...)(T args) 4916 if (T.length > 0) { return textImpl!wstring(args); } 4917 4918 ///ditto 4919 dstring dtext(T...)(T args) 4920 if (T.length > 0) { return textImpl!dstring(args); } 4921 4922 /// 4923 @safe unittest 4924 { 4925 assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c); 4926 assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w); 4927 assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d); 4928 } 4929 4930 @safe unittest 4931 { 4932 char c = 'h'; 4933 wchar w = '你'; 4934 dchar d = 'እ'; 4935 4936 assert( text(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"c); 4937 assert(wtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"w); 4938 assert(dtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"d); 4939 4940 string cs = "今日は"; 4941 wstring ws = "여보세요"; 4942 dstring ds = "Здравствуйте"; 4943 4944 assert( text(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"c); 4945 assert(wtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"w); 4946 assert(dtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"d); 4947 } 4948 4949 // Ensure that ranges are being printed as expected. 4950 @safe unittest 4951 { 4952 static struct Range 4953 { 4954 int counter = 0; 4955 4956 @safe pure nothrow @nogc: 4957 bool empty() const => (counter <= 0); 4958 int front() const => counter; 4959 void popFront() { --counter; } 4960 } 4961 4962 auto m = Range(2); 4963 assert(text(m) == "[2, 1]"); 4964 4965 const c = Range(3); 4966 assert(text(c) == "const(Range)(3)"); 4967 } 4968 4969 // Ensure that a usage pattern seen in libraries like "unit-threaded" works. 4970 @safe unittest 4971 { 4972 static final class Foo 4973 { 4974 override string toString() const @safe 4975 { 4976 return ":-)"; 4977 } 4978 } 4979 4980 const c = new Foo(); 4981 assert(text(c) == ":-)"); 4982 assert(text(c, " ") == ":-) "); 4983 } 4984 4985 // Ensure that classes are printed as expected. 4986 @system unittest 4987 { 4988 import std.string : endsWith, startsWith; 4989 4990 static final class Bar {} 4991 4992 // CTFE: `Bar` 4993 // Runtime: `std.conv.__unittest_L4875_C9.Bar` 4994 static assert(text( new Bar () ) == "Bar" ); 4995 assert(text( new Bar () ).endsWith ( ".Bar" )); 4996 static assert(text("=", new Bar (), ".") == "=Bar." ); 4997 assert(text("=", new Bar (), ".").endsWith ( ".Bar." )); 4998 static assert(text( new const(Bar)() ) == "const(Bar)" ); 4999 assert(text( new const(Bar)() ).startsWith( "const(" )); 5000 assert(text( new const(Bar)() ).endsWith ( ".Bar)" )); 5001 static assert(text("=", new const(Bar)(), ".") == "=const(Bar)." ); 5002 assert(text("=", new const(Bar)(), ".").startsWith("=const(" )); 5003 assert(text("=", new const(Bar)(), ".").endsWith ( ".Bar).")); 5004 } 5005 5006 // Ensure that various types are printed as expected. 5007 @safe unittest 5008 { 5009 import std.string : endsWith; 5010 5011 int dummy; 5012 5013 static struct Foo {} 5014 struct Bar { int i() @safe => dummy; } 5015 5016 static struct Point 5017 { 5018 int x; 5019 int y; 5020 } 5021 5022 struct Range 5023 { 5024 bool empty() => dummy > 9; 5025 int front() => dummy; 5026 void popFront() => cast(void) ++dummy; 5027 } 5028 5029 assert(text(null ) == "null" ); 5030 assert(text(null, null ) == "nullnull" ); 5031 assert(text(0, null, '\0') == "0null\x00"); 5032 5033 assert(text('\r','\n','\t','\x00') == "\r\n\t\0"); 5034 assert(text("\r\n\t\0" ) == "\r\n\t\0"); 5035 5036 assert(text( 3141, ) == "3141" ); 5037 assert(text(": ", 3141, '\0') == ": 3141\0"); 5038 assert(text( -3141, ) == "-3141" ); 5039 assert(text(": ", -3141, '\0') == ": -3141\0"); 5040 5041 () @trusted 5042 { 5043 int* pointer = cast(int*) 3141; 5044 assert(text( pointer, ) == "C45" ); 5045 assert(text(": ", pointer, '\0') == ": C45\0"); 5046 }(); 5047 5048 assert(text( 3.1415923, ) == "3.14159" ); 5049 assert(text(": ", 3.1415923, '\0') == ": 3.14159\0"); 5050 assert(text( 3.1415923f, ) == "3.14159" ); 5051 assert(text(": ", 3.1415923f, '\0') == ": 3.14159\0"); 5052 5053 assert(text( !3141, ) == "false" ); 5054 assert(text(": ", !3141, '\0') == ": false\0"); 5055 assert(text( !!3141, ) == "true" ); 5056 assert(text(": ", !!3141, '\0') == ": true\0" ); 5057 5058 assert(text( Foo(), ) == "Foo()" ); 5059 assert(text(": ", Foo(), '\0') == ": Foo()\0"); 5060 assert(text( const(Foo)(), ) == "const(Foo)()" ); 5061 assert(text(": ", const(Foo)(), '\0') == ": const(Foo)()\0"); 5062 5063 assert(text( Bar(), ) == "Bar()" ); 5064 assert(text(": ", Bar(), '\0') == ": Bar()\0"); 5065 assert(text( const(Bar)(), ) == "const(Bar)()" ); 5066 assert(text(": ", const(Bar)(), '\0') == ": const(Bar)()\0"); 5067 5068 assert(text( Point(3, 141), ) == "Point(3, 141)" ); 5069 assert(text(": ", Point(3, 141), '\0') == ": Point(3, 141)\0"); 5070 assert(text( const(Point)(3, 141), ) == "const(Point)(3, 141)" ); 5071 assert(text(": ", const(Point)(3, 141), '\0') == ": const(Point)(3, 141)\0"); 5072 assert(text( shared(Point)(3, 141), ) == "shared(Point)(3, 141)" ); 5073 assert(text(": ", shared(Point)(3, 141), '\0') == ": shared(Point)(3, 141)\0"); 5074 assert(text( immutable(Point)(3, 141), ) == "immutable(Point)(3, 141)" ); 5075 assert(text(": ", immutable(Point)(3, 141), '\0') == ": immutable(Point)(3, 141)\0"); 5076 5077 dummy = 0; 5078 assert(text( Range(), ) == "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" ); 5079 dummy = 0; 5080 assert(text(": ", Range(), '\0') == ": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\0"); 5081 assert(text( const(Range)(), ) == "const(Range)()" ); 5082 assert(text(": ", const(Range)(), '\0') == ": const(Range)()\0" ); 5083 5084 void function() @safe fn1; 5085 void delegate() @safe dg1; 5086 assert(text(fn1) == "null"); 5087 assert(text(dg1) == "void delegate() @safe"); 5088 5089 bool function(bool, int) @safe fn2; 5090 bool delegate(bool, int) @safe dg2; 5091 bool delegate(bool, int) @system dg3; 5092 bool delegate(bool, int) @trusted dg4; 5093 assert(text(fn2) == "null"); 5094 assert(text(dg2) == "bool delegate(bool, int) @safe"); 5095 assert(text(dg3) == "bool delegate(bool, int) @system"); 5096 assert(text(dg4) == "bool delegate(bool, int) @trusted"); 5097 } 5098 5099 /// Convenience functions for writing arguments to an output range as text. 5100 void writeText(Sink, T...)(ref Sink sink, T args) 5101 if (isOutputRange!(Sink, char) && T.length > 0) 5102 { 5103 sink.writeTextImpl!string(args); 5104 } 5105 5106 /// ditto 5107 void writeWText(Sink, T...)(ref Sink sink, T args) 5108 if (isOutputRange!(Sink, wchar) && T.length > 0) 5109 { 5110 sink.writeTextImpl!wstring(args); 5111 } 5112 5113 /// ditto 5114 void writeDText(Sink, T...)(ref Sink sink, T args) 5115 if (isOutputRange!(Sink, dchar) && T.length > 0) 5116 { 5117 sink.writeTextImpl!dstring(args); 5118 } 5119 5120 /// 5121 @safe unittest 5122 { 5123 import std.array : appender; 5124 5125 auto output = appender!string(); 5126 output.writeText("The answer is ", 42); 5127 5128 assert(output.data == "The answer is 42"); 5129 } 5130 5131 /// 5132 @safe unittest 5133 { 5134 import std.array : appender; 5135 5136 const color = "red"; 5137 auto output = appender!string(); 5138 output.writeText(i"My favorite color is $(color)"); 5139 5140 assert(output.data == "My favorite color is red"); 5141 } 5142 5143 @safe unittest 5144 { 5145 auto capp = appender!string(); 5146 auto wapp = appender!wstring(); 5147 auto dapp = appender!dstring(); 5148 5149 capp.writeText(42, ' ', 1.5, ": xyz"); 5150 wapp.writeWText(42, ' ', 1.5, ": xyz"); 5151 dapp.writeDText(42, ' ', 1.5, ": xyz"); 5152 5153 assert(capp.data == "42 1.5: xyz"c); 5154 assert(wapp.data == "42 1.5: xyz"w); 5155 assert(dapp.data == "42 1.5: xyz"d); 5156 } 5157 5158 // Check range API compliance using OutputRange interface 5159 @system unittest 5160 { 5161 import std.range.interfaces : OutputRange, outputRangeObject; 5162 import std.range : nullSink; 5163 5164 OutputRange!char testOutput = outputRangeObject!char(nullSink); 5165 testOutput.writeText(42, ' ', 1.5, ": xyz"); 5166 } 5167 5168 private S textImpl(S, U...)(U args) 5169 { 5170 static if (U.length == 0) 5171 { 5172 return null; 5173 } 5174 else static if (U.length == 1) 5175 { 5176 return to!S(args[0]); 5177 } 5178 else 5179 { 5180 import std.array : appender; 5181 import std.traits : isSomeChar, isSomeString; 5182 5183 auto app = appender!S(); 5184 5185 // assume that on average, parameters will have less 5186 // than 20 elements 5187 app.reserve(U.length * 20); 5188 app.writeTextImpl!S(args); 5189 return app.data; 5190 } 5191 } 5192 5193 private void writeTextImpl(S, Sink, U...)(ref Sink sink, U args) 5194 if (isSomeString!S && isOutputRange!(Sink, ElementEncodingType!S)) 5195 { 5196 // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209 5197 static foreach (arg; args) 5198 { 5199 static if ( 5200 isSomeChar!(typeof(arg)) 5201 || isSomeString!(typeof(arg)) 5202 || ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) ) 5203 ) 5204 put(sink, arg); 5205 else static if ( 5206 5207 is(immutable typeof(arg) == immutable uint) || is(immutable typeof(arg) == immutable ulong) || 5208 is(immutable typeof(arg) == immutable int) || is(immutable typeof(arg) == immutable long) 5209 ) 5210 // https://issues.dlang.org/show_bug.cgi?id=17712#c15 5211 put(sink, textImpl!(S)(arg)); 5212 else 5213 put(sink, to!S(arg)); 5214 } 5215 } 5216 5217 5218 /*************************************************************** 5219 The `octal` facility provides a means to declare a number in base 8. 5220 Using `octal!177` or `octal!"177"` for 127 represented in octal 5221 (same as 0177 in C). 5222 5223 The rules for strings are the usual for literals: If it can fit in an 5224 `int`, it is an `int`. Otherwise, it is a `long`. But, if the 5225 user specifically asks for a `long` with the `L` suffix, always 5226 give the `long`. Give an unsigned iff it is asked for with the $(D 5227 U) or `u` suffix. _Octals created from integers preserve the type 5228 of the passed-in integral. 5229 5230 See_Also: 5231 $(LREF parse) for parsing octal strings at runtime. 5232 */ 5233 template octal(string num) 5234 if (isOctalLiteral(num)) 5235 { 5236 static if ((octalFitsInInt!num && !literalIsLong!num) && !literalIsUnsigned!num) 5237 enum octal = octal!int(num); 5238 else static if ((!octalFitsInInt!num || literalIsLong!num) && !literalIsUnsigned!num) 5239 enum octal = octal!long(num); 5240 else static if ((octalFitsInInt!num && !literalIsLong!num) && literalIsUnsigned!num) 5241 enum octal = octal!uint(num); 5242 else static if ((!octalFitsInInt!(num) || literalIsLong!(num)) && literalIsUnsigned!(num)) 5243 enum octal = octal!ulong(num); 5244 else 5245 static assert(false, "Unusable input " ~ num); 5246 } 5247 5248 /// Ditto 5249 template octal(alias decimalInteger) 5250 if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger))) 5251 { 5252 enum octal = convertToOctal(decimalInteger); 5253 } 5254 5255 /// 5256 @safe unittest 5257 { 5258 // Same as 0177 5259 auto a = octal!177; 5260 // octal is a compile-time device 5261 enum b = octal!160; 5262 // Create an unsigned octal 5263 auto c = octal!"1_000_000u"; 5264 // Leading zeros are allowed when converting from a string 5265 auto d = octal!"0001_200_000"; 5266 } 5267 5268 /************************************* 5269 * Convert a decimal integer to an octal integer with the same digits. 5270 * Params: 5271 * i = integer to convert 5272 * Returns: 5273 * octal integer with the same type and same digits 5274 */ 5275 private T convertToOctal(T)(T i) 5276 { 5277 assert((i % 10) < 8); 5278 return i ? convertToOctal(i / 10) * 8 + i % 10 : 0; 5279 } 5280 5281 /* 5282 Takes a string, num, which is an octal literal, and returns its 5283 value, in the type T specified. 5284 */ 5285 private T octal(T)(const string num) 5286 { 5287 assert(isOctalLiteral(num), num ~ " is not an octal literal"); 5288 5289 T value = 0; 5290 5291 foreach (const char s; num) 5292 { 5293 if (s < '0' || s > '7') // we only care about digits; skip the rest 5294 // safe to skip - this is checked out in the assert so these 5295 // are just suffixes 5296 continue; 5297 5298 value *= 8; 5299 value += s - '0'; 5300 } 5301 5302 return value; 5303 } 5304 5305 @safe unittest 5306 { 5307 int a = octal!int("10"); 5308 assert(a == 8); 5309 5310 int b = octal!int("000137"); 5311 assert(b == 95); 5312 } 5313 5314 /* 5315 Take a look at int.max and int.max+1 in octal and the logic for this 5316 function follows directly. 5317 */ 5318 private template octalFitsInInt(string octalNum) 5319 { 5320 // note it is important to strip the literal of all 5321 // non-numbers. kill the suffix and underscores lest they mess up 5322 // the number of digits here that we depend on. 5323 enum bool octalFitsInInt = strippedOctalLiteral(octalNum).length < 11 || 5324 strippedOctalLiteral(octalNum).length == 11 && 5325 strippedOctalLiteral(octalNum)[0] == '1'; 5326 } 5327 5328 private string strippedOctalLiteral(string original) 5329 { 5330 string stripped = ""; 5331 bool leading_zeros = true; 5332 foreach (c; original) 5333 { 5334 if (!('0' <= c && c <= '7')) 5335 continue; 5336 if (c == '0') 5337 { 5338 if (leading_zeros) 5339 continue; 5340 } 5341 else 5342 { 5343 leading_zeros = false; 5344 } 5345 stripped ~= c; 5346 } 5347 if (stripped.length == 0) 5348 { 5349 assert(leading_zeros); 5350 return "0"; 5351 } 5352 return stripped; 5353 } 5354 5355 @safe unittest 5356 { 5357 static assert(strippedOctalLiteral("7") == "7"); 5358 static assert(strippedOctalLiteral("123") == "123"); 5359 static assert(strippedOctalLiteral("00123") == "123"); 5360 static assert(strippedOctalLiteral("01230") == "1230"); 5361 static assert(strippedOctalLiteral("0") == "0"); 5362 static assert(strippedOctalLiteral("00_000") == "0"); 5363 static assert(strippedOctalLiteral("000_000_12_300") == "12300"); 5364 } 5365 5366 private template literalIsLong(string num) 5367 { 5368 static if (num.length > 1) 5369 // can be xxL or xxLu according to spec 5370 enum literalIsLong = (num[$-1] == 'L' || num[$-2] == 'L'); 5371 else 5372 enum literalIsLong = false; 5373 } 5374 5375 private template literalIsUnsigned(string num) 5376 { 5377 static if (num.length > 1) 5378 // can be xxU or xxUL according to spec 5379 enum literalIsUnsigned = (num[$-1] == 'u' || num[$-2] == 'u') 5380 // both cases are allowed too 5381 || (num[$-1] == 'U' || num[$-2] == 'U'); 5382 else 5383 enum literalIsUnsigned = false; 5384 } 5385 5386 /* 5387 Returns if the given string is a correctly formatted octal literal. 5388 5389 The format is specified in spec/lex.html. The leading zeros are allowed, 5390 but not required. 5391 */ 5392 @safe pure nothrow @nogc 5393 private bool isOctalLiteral(const string num) 5394 { 5395 if (num.length == 0) 5396 return false; 5397 5398 // Must start with a digit. 5399 if (num[0] < '0' || num[0] > '7') 5400 return false; 5401 5402 foreach (i, c; num) 5403 { 5404 if (('0' <= c && c <= '7') || c == '_') // a legal character 5405 continue; 5406 5407 if (i < num.length - 2) 5408 return false; 5409 5410 // gotta check for those suffixes 5411 if (c != 'U' && c != 'u' && c != 'L') 5412 return false; 5413 if (i != num.length - 1) 5414 { 5415 // if we're not the last one, the next one must 5416 // also be a suffix to be valid 5417 char c2 = num[$-1]; 5418 if (c2 != 'U' && c2 != 'u' && c2 != 'L') 5419 return false; // spam at the end of the string 5420 if (c2 == c) 5421 return false; // repeats are disallowed 5422 } 5423 } 5424 5425 return true; 5426 } 5427 5428 @safe unittest 5429 { 5430 // ensure that you get the right types, even with embedded underscores 5431 auto w = octal!"100_000_000_000"; 5432 static assert(!is(typeof(w) == int)); 5433 auto w2 = octal!"1_000_000_000"; 5434 static assert(is(typeof(w2) == int)); 5435 5436 static assert(octal!"45" == 37); 5437 static assert(octal!"0" == 0); 5438 static assert(octal!"7" == 7); 5439 static assert(octal!"10" == 8); 5440 static assert(octal!"666" == 438); 5441 static assert(octal!"0004001" == 2049); 5442 static assert(octal!"00" == 0); 5443 static assert(octal!"0_0" == 0); 5444 5445 static assert(octal!45 == 37); 5446 static assert(octal!0 == 0); 5447 static assert(octal!7 == 7); 5448 static assert(octal!10 == 8); 5449 static assert(octal!666 == 438); 5450 5451 static assert(octal!"66_6" == 438); 5452 static assert(octal!"0_0_66_6" == 438); 5453 5454 static assert(octal!2520046213 == 356535435); 5455 static assert(octal!"2520046213" == 356535435); 5456 5457 static assert(octal!17777777777 == int.max); 5458 5459 static assert(!__traits(compiles, octal!823)); 5460 5461 static assert(!__traits(compiles, octal!"823")); 5462 5463 static assert(!__traits(compiles, octal!"_823")); 5464 static assert(!__traits(compiles, octal!"spam")); 5465 static assert(!__traits(compiles, octal!"77%")); 5466 5467 static assert(is(typeof(octal!"17777777777") == int)); 5468 static assert(octal!"17777777777" == int.max); 5469 5470 static assert(is(typeof(octal!"20000000000U") == ulong)); // Shouldn't this be uint? 5471 static assert(octal!"20000000000" == uint(int.max) + 1); 5472 5473 static assert(is(typeof(octal!"777777777777777777777") == long)); 5474 static assert(octal!"777777777777777777777" == long.max); 5475 5476 static assert(is(typeof(octal!"1000000000000000000000U") == ulong)); 5477 static assert(octal!"1000000000000000000000" == ulong(long.max) + 1); 5478 5479 int a; 5480 long b; 5481 5482 // biggest value that should fit in an it 5483 a = octal!"17777777777"; 5484 assert(a == int.max); 5485 // should not fit in the int 5486 static assert(!__traits(compiles, a = octal!"20000000000")); 5487 // ... but should fit in a long 5488 b = octal!"20000000000"; 5489 assert(b == 1L + int.max); 5490 5491 b = octal!"1L"; 5492 assert(b == 1); 5493 b = octal!1L; 5494 assert(b == 1); 5495 } 5496 5497 // emplace() used to be here but was moved to druntime 5498 public import core.lifetime : emplace; 5499 5500 // https://issues.dlang.org/show_bug.cgi?id=9559 5501 @safe unittest 5502 { 5503 import std.algorithm.iteration : map; 5504 import std.array : array; 5505 import std.typecons : Nullable; 5506 alias I = Nullable!int; 5507 auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); 5508 auto asArray = array(ints); 5509 } 5510 5511 @system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org 5512 { 5513 import std.array : array; 5514 import std.datetime : SysTime, UTC; 5515 import std.math.traits : isNaN; 5516 5517 static struct A 5518 { 5519 double i; 5520 } 5521 5522 static struct B 5523 { 5524 invariant() 5525 { 5526 if (j == 0) 5527 assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?"); 5528 else 5529 assert(!a.i.isNaN()); 5530 } 5531 SysTime when; // comment this line avoid the breakage 5532 int j; 5533 A a; 5534 } 5535 5536 B b1 = B.init; 5537 assert(&b1); // verify that default eyes invariants are ok; 5538 5539 auto b2 = B(SysTime(0, UTC()), 1, A(1)); 5540 assert(&b2); 5541 auto b3 = B(SysTime(0, UTC()), 1, A(1)); 5542 assert(&b3); 5543 5544 auto arr = [b2, b3]; 5545 5546 assert(arr[0].j == 1); 5547 assert(arr[1].j == 1); 5548 auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good 5549 } 5550 5551 @safe unittest 5552 { 5553 import std.algorithm.comparison : equal; 5554 import std.algorithm.iteration : map; 5555 // Check fix for https://issues.dlang.org/show_bug.cgi?id=2971 5556 assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345])); 5557 } 5558 5559 // Undocumented for the time being 5560 void toTextRange(T, W)(T value, W writer) 5561 if (isIntegral!T && isOutputRange!(W, char)) 5562 { 5563 import core.internal.string : SignedStringBuf, signedToTempString, 5564 UnsignedStringBuf, unsignedToTempString; 5565 5566 if (value < 0) 5567 { 5568 SignedStringBuf buf = void; 5569 put(writer, signedToTempString(value, buf)); 5570 } 5571 else 5572 { 5573 UnsignedStringBuf buf = void; 5574 put(writer, unsignedToTempString(value, buf)); 5575 } 5576 } 5577 5578 @safe unittest 5579 { 5580 import std.array : appender; 5581 auto result = appender!(char[])(); 5582 toTextRange(-1, result); 5583 assert(result.data == "-1"); 5584 } 5585 5586 5587 /** 5588 Returns the corresponding _unsigned value for `x` (e.g. if `x` has type 5589 `int`, it returns $(D cast(uint) x)). The advantage compared to the cast 5590 is that you do not need to rewrite the cast if `x` later changes type 5591 (e.g from `int` to `long`). 5592 5593 Note that the result is always mutable even if the original type was const 5594 or immutable. In order to retain the constness, use $(REF Unsigned, std,traits). 5595 */ 5596 auto unsigned(T)(T x) 5597 if (isIntegral!T) 5598 { 5599 return cast() cast(Unsigned!T) x; 5600 } 5601 5602 /// 5603 @safe unittest 5604 { 5605 import std.traits : Unsigned; 5606 immutable int s = 42; 5607 auto u1 = unsigned(s); //not qualified 5608 static assert(is(typeof(u1) == uint)); 5609 Unsigned!(typeof(s)) u2 = unsigned(s); //same qualification 5610 static assert(is(typeof(u2) == immutable uint)); 5611 immutable u3 = unsigned(s); //explicitly qualified 5612 } 5613 5614 /// Ditto 5615 auto unsigned(T)(T x) 5616 if (isSomeChar!T) 5617 { 5618 // All characters are unsigned 5619 static assert(T.min == 0, T.stringof ~ ".min must be zero"); 5620 return cast() x; 5621 } 5622 5623 @safe unittest 5624 { 5625 static foreach (T; AliasSeq!(byte, ubyte)) 5626 { 5627 static assert(is(typeof(unsigned(cast(T) 1)) == ubyte)); 5628 static assert(is(typeof(unsigned(cast(const T) 1)) == ubyte)); 5629 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ubyte)); 5630 } 5631 5632 static foreach (T; AliasSeq!(short, ushort)) 5633 { 5634 static assert(is(typeof(unsigned(cast(T) 1)) == ushort)); 5635 static assert(is(typeof(unsigned(cast(const T) 1)) == ushort)); 5636 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ushort)); 5637 } 5638 5639 static foreach (T; AliasSeq!(int, uint)) 5640 { 5641 static assert(is(typeof(unsigned(cast(T) 1)) == uint)); 5642 static assert(is(typeof(unsigned(cast(const T) 1)) == uint)); 5643 static assert(is(typeof(unsigned(cast(immutable T) 1)) == uint)); 5644 } 5645 5646 static foreach (T; AliasSeq!(long, ulong)) 5647 { 5648 static assert(is(typeof(unsigned(cast(T) 1)) == ulong)); 5649 static assert(is(typeof(unsigned(cast(const T) 1)) == ulong)); 5650 static assert(is(typeof(unsigned(cast(immutable T) 1)) == ulong)); 5651 } 5652 } 5653 5654 @safe unittest 5655 { 5656 static foreach (T; AliasSeq!(char, wchar, dchar)) 5657 { 5658 static assert(is(typeof(unsigned(cast(T)'A')) == T)); 5659 static assert(is(typeof(unsigned(cast(const T)'A')) == T)); 5660 static assert(is(typeof(unsigned(cast(immutable T)'A')) == T)); 5661 } 5662 } 5663 5664 5665 /** 5666 Returns the corresponding _signed value for `x` (e.g. if `x` has type 5667 `uint`, it returns $(D cast(int) x)). The advantage compared to the cast 5668 is that you do not need to rewrite the cast if `x` later changes type 5669 (e.g from `uint` to `ulong`). 5670 5671 Note that the result is always mutable even if the original type was const 5672 or immutable. In order to retain the constness, use $(REF Signed, std,traits). 5673 */ 5674 auto signed(T)(T x) 5675 if (isIntegral!T) 5676 { 5677 return cast() cast(Signed!T) x; 5678 } 5679 5680 /// 5681 @safe unittest 5682 { 5683 import std.traits : Signed; 5684 5685 immutable uint u = 42; 5686 auto s1 = signed(u); //not qualified 5687 static assert(is(typeof(s1) == int)); 5688 Signed!(typeof(u)) s2 = signed(u); //same qualification 5689 static assert(is(typeof(s2) == immutable int)); 5690 immutable s3 = signed(u); //explicitly qualified 5691 } 5692 5693 @system unittest 5694 { 5695 static foreach (T; AliasSeq!(byte, ubyte)) 5696 { 5697 static assert(is(typeof(signed(cast(T) 1)) == byte)); 5698 static assert(is(typeof(signed(cast(const T) 1)) == byte)); 5699 static assert(is(typeof(signed(cast(immutable T) 1)) == byte)); 5700 } 5701 5702 static foreach (T; AliasSeq!(short, ushort)) 5703 { 5704 static assert(is(typeof(signed(cast(T) 1)) == short)); 5705 static assert(is(typeof(signed(cast(const T) 1)) == short)); 5706 static assert(is(typeof(signed(cast(immutable T) 1)) == short)); 5707 } 5708 5709 static foreach (T; AliasSeq!(int, uint)) 5710 { 5711 static assert(is(typeof(signed(cast(T) 1)) == int)); 5712 static assert(is(typeof(signed(cast(const T) 1)) == int)); 5713 static assert(is(typeof(signed(cast(immutable T) 1)) == int)); 5714 } 5715 5716 static foreach (T; AliasSeq!(long, ulong)) 5717 { 5718 static assert(is(typeof(signed(cast(T) 1)) == long)); 5719 static assert(is(typeof(signed(cast(const T) 1)) == long)); 5720 static assert(is(typeof(signed(cast(immutable T) 1)) == long)); 5721 } 5722 } 5723 5724 // https://issues.dlang.org/show_bug.cgi?id=10874 5725 @safe unittest 5726 { 5727 enum Test { a = 0 } 5728 ulong l = 0; 5729 auto t = l.to!Test; 5730 } 5731 5732 // asOriginalType 5733 /** 5734 Returns the representation of an enumerated value, i.e. the value converted to 5735 the base type of the enumeration. 5736 */ 5737 OriginalType!E asOriginalType(E)(E value) 5738 if (is(E == enum)) 5739 { 5740 return value; 5741 } 5742 5743 /// 5744 @safe unittest 5745 { 5746 enum A { a = 42 } 5747 static assert(is(typeof(A.a.asOriginalType) == int)); 5748 assert(A.a.asOriginalType == 42); 5749 enum B : double { a = 43 } 5750 static assert(is(typeof(B.a.asOriginalType) == double)); 5751 assert(B.a.asOriginalType == 43); 5752 } 5753 5754 /** 5755 A wrapper on top of the built-in cast operator that allows one to restrict 5756 casting of the original type of the value. 5757 5758 A common issue with using a raw cast is that it may silently continue to 5759 compile even if the value's type has changed during refactoring, 5760 which breaks the initial assumption about the cast. 5761 5762 Params: 5763 From = The type to cast from. The programmer must ensure it is legal 5764 to make this cast. 5765 */ 5766 template castFrom(From) 5767 { 5768 /** 5769 Params: 5770 To = The type _to cast _to. 5771 value = The value _to cast. It must be of type `From`, 5772 otherwise a compile-time error is emitted. 5773 5774 Returns: 5775 the value after the cast, returned by reference if possible. 5776 */ 5777 auto ref to(To, T)(auto ref T value) @system 5778 { 5779 static assert( 5780 is(From == T), 5781 "the value to cast is not of specified type '" ~ From.stringof ~ 5782 "', it is of type '" ~ T.stringof ~ "'" 5783 ); 5784 5785 static assert( 5786 is(typeof(cast(To) value)), 5787 "can't cast from '" ~ From.stringof ~ "' to '" ~ To.stringof ~ "'" 5788 ); 5789 5790 return cast(To) value; 5791 } 5792 } 5793 5794 /// 5795 @system unittest 5796 { 5797 // Regular cast, which has been verified to be legal by the programmer: 5798 { 5799 long x; 5800 auto y = cast(int) x; 5801 } 5802 5803 // However this will still compile if 'x' is changed to be a pointer: 5804 { 5805 long* x; 5806 auto y = cast(int) x; 5807 } 5808 5809 // castFrom provides a more reliable alternative to casting: 5810 { 5811 long x; 5812 auto y = castFrom!long.to!int(x); 5813 } 5814 5815 // Changing the type of 'x' will now issue a compiler error, 5816 // allowing bad casts to be caught before it's too late: 5817 { 5818 long* x; 5819 static assert( 5820 !__traits(compiles, castFrom!long.to!int(x)) 5821 ); 5822 5823 // if cast is still needed, must be changed to: 5824 auto y = castFrom!(long*).to!int(x); 5825 } 5826 } 5827 5828 // https://issues.dlang.org/show_bug.cgi?id=16667 5829 @system unittest 5830 { 5831 ubyte[] a = ['a', 'b', 'c']; 5832 assert(castFrom!(ubyte[]).to!(string)(a) == "abc"); 5833 } 5834 5835 /** 5836 Check the correctness of a string for `hexString`. 5837 The result is true if and only if the input string is composed of whitespace 5838 characters (\f\n\r\t\v lineSep paraSep nelSep) and 5839 an even number of hexadecimal digits (regardless of the case). 5840 */ 5841 @safe pure @nogc 5842 private bool isHexLiteral(String)(scope const String hexData) 5843 { 5844 import std.ascii : isHexDigit; 5845 import std.uni : lineSep, paraSep, nelSep; 5846 size_t i; 5847 foreach (const dchar c; hexData) 5848 { 5849 switch (c) 5850 { 5851 case ' ': 5852 case '\t': 5853 case '\v': 5854 case '\f': 5855 case '\r': 5856 case '\n': 5857 case lineSep: 5858 case paraSep: 5859 case nelSep: 5860 continue; 5861 5862 default: 5863 break; 5864 } 5865 if (c.isHexDigit) 5866 ++i; 5867 else 5868 return false; 5869 } 5870 return !(i & 1); 5871 } 5872 5873 @safe unittest 5874 { 5875 // test all the hex digits 5876 static assert( ("0123456789abcdefABCDEF").isHexLiteral); 5877 // empty or white strings are not valid 5878 static assert( "\r\n\t".isHexLiteral); 5879 // but are accepted if the count of hex digits is even 5880 static assert( "A\r\n\tB".isHexLiteral); 5881 } 5882 5883 @safe unittest 5884 { 5885 import std.ascii; 5886 // empty/whites 5887 static assert( "".isHexLiteral); 5888 static assert( " \r".isHexLiteral); 5889 static assert( whitespace.isHexLiteral); 5890 static assert( ""w.isHexLiteral); 5891 static assert( " \r"w.isHexLiteral); 5892 static assert( ""d.isHexLiteral); 5893 static assert( " \r"d.isHexLiteral); 5894 static assert( "\u2028\u2029\u0085"d.isHexLiteral); 5895 // odd x strings 5896 static assert( !("5" ~ whitespace).isHexLiteral); 5897 static assert( !"123".isHexLiteral); 5898 static assert( !"1A3".isHexLiteral); 5899 static assert( !"1 23".isHexLiteral); 5900 static assert( !"\r\n\tC".isHexLiteral); 5901 static assert( !"123"w.isHexLiteral); 5902 static assert( !"1A3"w.isHexLiteral); 5903 static assert( !"1 23"w.isHexLiteral); 5904 static assert( !"\r\n\tC"w.isHexLiteral); 5905 static assert( !"123"d.isHexLiteral); 5906 static assert( !"1A3"d.isHexLiteral); 5907 static assert( !"1 23"d.isHexLiteral); 5908 static assert( !"\r\n\tC"d.isHexLiteral); 5909 // even x strings with invalid charset 5910 static assert( !"12gG".isHexLiteral); 5911 static assert( !"2A 3q".isHexLiteral); 5912 static assert( !"12gG"w.isHexLiteral); 5913 static assert( !"2A 3q"w.isHexLiteral); 5914 static assert( !"12gG"d.isHexLiteral); 5915 static assert( !"2A 3q"d.isHexLiteral); 5916 // valid x strings 5917 static assert( ("5A" ~ whitespace).isHexLiteral); 5918 static assert( ("5A 01A C FF de 1b").isHexLiteral); 5919 static assert( ("0123456789abcdefABCDEF").isHexLiteral); 5920 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF").isHexLiteral); 5921 static assert( ("5A 01A C FF de 1b"w).isHexLiteral); 5922 static assert( ("0123456789abcdefABCDEF"w).isHexLiteral); 5923 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"w).isHexLiteral); 5924 static assert( ("5A 01A C FF de 1b"d).isHexLiteral); 5925 static assert( ("0123456789abcdefABCDEF"d).isHexLiteral); 5926 static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"d).isHexLiteral); 5927 // library version allows what's pointed by https://issues.dlang.org/show_bug.cgi?id=10454 5928 static assert( ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").isHexLiteral); 5929 } 5930 5931 /** 5932 Converts a hex literal to a string at compile time. 5933 5934 Takes a string made of hexadecimal digits and returns 5935 the matching string by converting each pair of digits to a character. 5936 The input string can also include white characters, which can be used 5937 to keep the literal string readable in the source code. 5938 5939 The function is intended to replace the hexadecimal literal strings 5940 starting with `'x'`, which could be removed to simplify the core language. 5941 5942 Params: 5943 hexData = string to be converted. 5944 5945 Returns: 5946 a `string`, a `wstring` or a `dstring`, according to the type of hexData. 5947 5948 See_Also: 5949 Use $(REF fromHexString, std, digest) for run time conversions. 5950 Note, these functions are not drop-in replacements and have different 5951 input requirements. 5952 This template inherits its data syntax from builtin 5953 $(LINK2 $(ROOT_DIR)spec/lex.html#hex_string, hex strings). 5954 See $(REF fromHexString, std, digest) for its own respective requirements. 5955 */ 5956 template hexString(string hexData) 5957 if (hexData.isHexLiteral) 5958 { 5959 enum hexString = mixin(hexToString(hexData)); 5960 } 5961 5962 /// ditto 5963 template hexString(wstring hexData) 5964 if (hexData.isHexLiteral) 5965 { 5966 enum wstring hexString = mixin(hexToString(hexData)); 5967 } 5968 5969 /// ditto 5970 template hexString(dstring hexData) 5971 if (hexData.isHexLiteral) 5972 { 5973 enum dstring hexString = mixin(hexToString(hexData)); 5974 } 5975 5976 /// 5977 @safe unittest 5978 { 5979 // conversion at compile time 5980 auto string1 = hexString!"304A314B"; 5981 assert(string1 == "0J1K"); 5982 auto string2 = hexString!"304A314B"w; 5983 assert(string2 == "0J1K"w); 5984 auto string3 = hexString!"304A314B"d; 5985 assert(string3 == "0J1K"d); 5986 } 5987 5988 @safe nothrow pure private 5989 { 5990 /* These are meant to be used with CTFE. 5991 * They cause the instantiations of hexStrLiteral() 5992 * to be in Phobos, not user code. 5993 */ 5994 string hexToString(string s) 5995 { 5996 return hexStrLiteral(s); 5997 } 5998 5999 wstring hexToString(wstring s) 6000 { 6001 return hexStrLiteral(s); 6002 } 6003 6004 dstring hexToString(dstring s) 6005 { 6006 return hexStrLiteral(s); 6007 } 6008 } 6009 6010 /* 6011 Turn a hexadecimal string into a regular string literal. 6012 I.e. "dead beef" is transformed into "\xde\xad\xbe\xef" 6013 suitable for use in a mixin. 6014 Params: 6015 hexData is string, wstring, or dstring and validated by isHexLiteral() 6016 */ 6017 @trusted nothrow pure 6018 private auto hexStrLiteral(String)(scope String hexData) 6019 { 6020 import std.ascii : isHexDigit; 6021 alias C = Unqual!(ElementEncodingType!String); // char, wchar or dchar 6022 C[] result; 6023 result.length = 1 + hexData.length * 2 + 1; // don't forget the " " 6024 /* Use a pointer because we know it won't overrun, 6025 * and this will reduce the size of the function substantially 6026 * by not doing the array bounds checks. 6027 * This is why this function is @trusted. 6028 */ 6029 auto r = result.ptr; 6030 r[0] = '"'; 6031 size_t cnt = 0; 6032 foreach (c; hexData) 6033 { 6034 if (c.isHexDigit) 6035 { 6036 if ((cnt & 1) == 0) 6037 { 6038 r[1 + cnt] = '\\'; 6039 r[1 + cnt + 1] = 'x'; 6040 cnt += 2; 6041 } 6042 r[1 + cnt] = c; 6043 ++cnt; 6044 } 6045 } 6046 r[1 + cnt] = '"'; 6047 result.length = 1 + cnt + 1; // trim off any excess length 6048 return result; 6049 } 6050 6051 6052 @safe unittest 6053 { 6054 // compile time 6055 assert(hexString!"46 47 48 49 4A 4B" == "FGHIJK"); 6056 assert(hexString!"30\r\n\t\f\v31 32 33 32 31 30" == "0123210"); 6057 assert(hexString!"ab cd" == hexString!"ABCD"); 6058 } 6059 6060 6061 /** 6062 * Convert integer to a range of characters. 6063 * Intended to be lightweight and fast. 6064 * 6065 * Params: 6066 * radix = 2, 8, 10, 16 6067 * Char = character type for output 6068 * letterCase = lower for deadbeef, upper for DEADBEEF 6069 * value = integer to convert. Can be ubyte, ushort, uint or ulong. If radix 6070 * is 10, can also be byte, short, int or long. 6071 * Returns: 6072 * Random access range with slicing and everything 6073 */ 6074 6075 auto toChars(ubyte radix = 10, Char = char, LetterCase letterCase = LetterCase.lower, T)(T value) 6076 pure nothrow @nogc @safe 6077 if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) && 6078 isIntegral!T && (radix == 10 || isUnsigned!T)) 6079 { 6080 alias UT = Unqual!T; 6081 6082 static if (radix == 10) 6083 { 6084 /* uint.max is 42_9496_7295 6085 * int.max is 21_4748_3647 6086 * ulong.max is 1844_6744_0737_0955_1615 6087 * long.max is 922_3372_0368_5477_5807 6088 */ 6089 static struct Result 6090 { 6091 void initialize(UT value) 6092 { 6093 import core.internal.string : signedToTempString, unsignedToTempString; 6094 6095 char[] t = value < 0 6096 ? signedToTempString!(10, false, char)(value, buf) 6097 : unsignedToTempString!(10, false, char)(value, buf); 6098 6099 lwr = cast(uint) (buf.length - t.length); 6100 upr = cast(uint) buf.length; 6101 } 6102 6103 @property size_t length() { return upr - lwr; } 6104 6105 alias opDollar = length; 6106 6107 @property bool empty() { return upr == lwr; } 6108 6109 @property Char front() { return buf[lwr]; } 6110 6111 void popFront() { ++lwr; } 6112 6113 @property Char back() { return buf[upr - 1]; } 6114 6115 void popBack() { --upr; } 6116 6117 @property Result save() { return this; } 6118 6119 Char opIndex(size_t i) { return buf[lwr + i]; } 6120 6121 Result opSlice(size_t lwr, size_t upr) 6122 { 6123 Result result = void; 6124 result.buf = buf; 6125 result.lwr = cast(uint)(this.lwr + lwr); 6126 result.upr = cast(uint)(this.lwr + upr); 6127 return result; 6128 } 6129 6130 private: 6131 uint lwr = void, upr = void; 6132 char[(UT.sizeof == 4) ? 10 + isSigned!T : 20] buf = void; 6133 } 6134 6135 Result result; 6136 result.initialize(value); 6137 return result; 6138 } 6139 else 6140 { 6141 static if (radix == 2) 6142 enum SHIFT = 1; 6143 else static if (radix == 8) 6144 enum SHIFT = 3; 6145 else static if (radix == 16) 6146 enum SHIFT = 4; 6147 else 6148 static assert(false, "radix must be 2, 8, 10, or 16"); 6149 static struct Result 6150 { 6151 this(UT value) 6152 { 6153 this.value = value; 6154 6155 ubyte len = 1; 6156 while (value >>>= SHIFT) 6157 ++len; 6158 this.len = len; 6159 } 6160 6161 @property size_t length() { return len; } 6162 6163 @property bool empty() { return len == 0; } 6164 6165 @property Char front() { return opIndex(0); } 6166 6167 void popFront() { --len; } 6168 6169 @property Char back() { return opIndex(len - 1); } 6170 6171 void popBack() 6172 { 6173 value >>>= SHIFT; 6174 --len; 6175 } 6176 6177 @property Result save() { return this; } 6178 6179 Char opIndex(size_t i) 6180 { 6181 Char c = (value >>> ((len - i - 1) * SHIFT)) & ((1 << SHIFT) - 1); 6182 return cast(Char)((radix < 10 || c < 10) ? c + '0' 6183 : (letterCase == LetterCase.upper ? c + 'A' - 10 6184 : c + 'a' - 10)); 6185 } 6186 6187 Result opSlice(size_t lwr, size_t upr) 6188 { 6189 Result result = void; 6190 result.value = value >>> ((len - upr) * SHIFT); 6191 result.len = cast(ubyte)(upr - lwr); 6192 return result; 6193 } 6194 6195 private: 6196 UT value; 6197 ubyte len; 6198 } 6199 6200 return Result(value); 6201 } 6202 } 6203 6204 /// 6205 @safe unittest 6206 { 6207 import std.algorithm.comparison : equal; 6208 6209 assert(toChars(1).equal("1")); 6210 assert(toChars(1_000_000).equal("1000000")); 6211 6212 assert(toChars!(2)(2U).equal("10")); 6213 assert(toChars!(16)(255U).equal("ff")); 6214 assert(toChars!(16, char, LetterCase.upper)(255U).equal("FF")); 6215 } 6216 6217 6218 @safe unittest 6219 { 6220 import std.array; 6221 import std.range; 6222 6223 assert(toChars(123) == toChars(123)); 6224 6225 { 6226 assert(toChars!2(ubyte(0)).array == "0"); 6227 assert(toChars!2(ushort(0)).array == "0"); 6228 assert(toChars!2(0u).array == "0"); 6229 assert(toChars!2(0Lu).array == "0"); 6230 assert(toChars!2(ubyte(1)).array == "1"); 6231 assert(toChars!2(ushort(1)).array == "1"); 6232 assert(toChars!2(1u).array == "1"); 6233 assert(toChars!2(1Lu).array == "1"); 6234 6235 auto r = toChars!2(2u); 6236 assert(r.length == 2); 6237 assert(r[0] == '1'); 6238 assert(r[1 .. 2].array == "0"); 6239 auto s = r.save; 6240 assert(r.array == "10"); 6241 assert(s.retro.array == "01"); 6242 } 6243 { 6244 assert(toChars!8(ubyte(0)).array == "0"); 6245 assert(toChars!8(ushort(0)).array == "0"); 6246 assert(toChars!8(0u).array == "0"); 6247 assert(toChars!8(0Lu).array == "0"); 6248 assert(toChars!8(1u).array == "1"); 6249 assert(toChars!8(1234567Lu).array == "4553207"); 6250 assert(toChars!8(ubyte.max).array == "377"); 6251 assert(toChars!8(ushort.max).array == "177777"); 6252 6253 auto r = toChars!8(8u); 6254 assert(r.length == 2); 6255 assert(r[0] == '1'); 6256 assert(r[1 .. 2].array == "0"); 6257 auto s = r.save; 6258 assert(r.array == "10"); 6259 assert(s.retro.array == "01"); 6260 } 6261 { 6262 assert(toChars!10(ubyte(0)).array == "0"); 6263 assert(toChars!10(ushort(0)).array == "0"); 6264 assert(toChars!10(0u).array == "0"); 6265 assert(toChars!10(0Lu).array == "0"); 6266 assert(toChars!10(1u).array == "1"); 6267 assert(toChars!10(1234567Lu).array == "1234567"); 6268 assert(toChars!10(ubyte.max).array == "255"); 6269 assert(toChars!10(ushort.max).array == "65535"); 6270 assert(toChars!10(uint.max).array == "4294967295"); 6271 assert(toChars!10(ulong.max).array == "18446744073709551615"); 6272 6273 auto r = toChars(10u); 6274 assert(r.length == 2); 6275 assert(r[0] == '1'); 6276 assert(r[1 .. 2].array == "0"); 6277 auto s = r.save; 6278 assert(r.array == "10"); 6279 assert(s.retro.array == "01"); 6280 } 6281 { 6282 assert(toChars!10(0).array == "0"); 6283 assert(toChars!10(0L).array == "0"); 6284 assert(toChars!10(1).array == "1"); 6285 assert(toChars!10(1234567L).array == "1234567"); 6286 assert(toChars!10(byte.max).array == "127"); 6287 assert(toChars!10(short.max).array == "32767"); 6288 assert(toChars!10(int.max).array == "2147483647"); 6289 assert(toChars!10(long.max).array == "9223372036854775807"); 6290 assert(toChars!10(-byte.max).array == "-127"); 6291 assert(toChars!10(-short.max).array == "-32767"); 6292 assert(toChars!10(-int.max).array == "-2147483647"); 6293 assert(toChars!10(-long.max).array == "-9223372036854775807"); 6294 assert(toChars!10(byte.min).array == "-128"); 6295 assert(toChars!10(short.min).array == "-32768"); 6296 assert(toChars!10(int.min).array == "-2147483648"); 6297 assert(toChars!10(long.min).array == "-9223372036854775808"); 6298 6299 auto r = toChars!10(10); 6300 assert(r.length == 2); 6301 assert(r[0] == '1'); 6302 assert(r[1 .. 2].array == "0"); 6303 auto s = r.save; 6304 assert(r.array == "10"); 6305 assert(s.retro.array == "01"); 6306 } 6307 { 6308 assert(toChars!(16)(0u).array == "0"); 6309 assert(toChars!(16)(0Lu).array == "0"); 6310 assert(toChars!(16)(10u).array == "a"); 6311 assert(toChars!(16, char, LetterCase.upper)(0x12AF34567Lu).array == "12AF34567"); 6312 assert(toChars!(16)(ubyte(0)).array == "0"); 6313 assert(toChars!(16)(ushort(0)).array == "0"); 6314 assert(toChars!(16)(ubyte.max).array == "ff"); 6315 assert(toChars!(16)(ushort.max).array == "ffff"); 6316 6317 auto r = toChars!(16)(16u); 6318 assert(r.length == 2); 6319 assert(r[0] == '1'); 6320 assert(r[1 .. 2].array == "0"); 6321 auto s = r.save; 6322 assert(r.array == "10"); 6323 assert(s.retro.array == "01"); 6324 } 6325 } 6326 6327 @safe unittest // opSlice (https://issues.dlang.org/show_bug.cgi?id=16192) 6328 { 6329 import std.meta : AliasSeq; 6330 6331 static struct Test { ubyte radix; uint number; } 6332 6333 alias tests = AliasSeq!( 6334 Test(2, 0b1_0110_0111u), 6335 Test(2, 0b10_1100_1110u), 6336 Test(8, octal!123456701u), 6337 Test(8, octal!1234567012u), 6338 Test(10, 123456789u), 6339 Test(10, 1234567890u), 6340 Test(16, 0x789ABCDu), 6341 Test(16, 0x789ABCDEu), 6342 ); 6343 6344 foreach (test; tests) 6345 { 6346 enum ubyte radix = test.radix; 6347 auto original = toChars!radix(test.number); 6348 6349 // opSlice vs popFront 6350 auto r = original.save; 6351 size_t i = 0; 6352 for (; !r.empty; r.popFront(), ++i) 6353 { 6354 assert(original[i .. original.length].tupleof == r.tupleof); 6355 // tupleof is used to work around https://issues.dlang.org/show_bug.cgi?id=16216. 6356 } 6357 6358 // opSlice vs popBack 6359 r = original.save; 6360 i = 0; 6361 for (; !r.empty; r.popBack(), ++i) 6362 { 6363 assert(original[0 .. original.length - i].tupleof == r.tupleof); 6364 } 6365 6366 // opSlice vs both popFront and popBack 6367 r = original.save; 6368 i = 0; 6369 for (; r.length >= 2; r.popFront(), r.popBack(), ++i) 6370 { 6371 assert(original[i .. original.length - i].tupleof == r.tupleof); 6372 } 6373 } 6374 } 6375 6376 // Converts an unsigned integer to a compile-time string constant. 6377 package enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length]; 6378 6379 // Check that .stringof does what we expect, since it's not guaranteed by the 6380 // language spec. 6381 @safe /*@betterC*/ unittest 6382 { 6383 assert(toCtString!0 == "0"); 6384 assert(toCtString!123456 == "123456"); 6385 } 6386 6387 /** 6388 * Takes the raw bits of a value and reinterprets them as a different type. 6389 * 6390 * Params: 6391 * T = the new type. 6392 * value = the value to reinterpret. 6393 * 6394 * Returns: a reference to the reinterpreted value. 6395 */ 6396 pragma(inline, true) 6397 ref T bitCast(T, S)(ref S value) 6398 if (T.sizeof <= S.sizeof) 6399 { 6400 return *cast(T*) &value; 6401 } 6402 6403 /// 6404 @safe unittest 6405 { 6406 uint n = 0xDEADBEEF; 6407 6408 version (LittleEndian) 6409 assert(n.bitCast!(ubyte[4]) == [0xEF, 0xBE, 0xAD, 0xDE]); 6410 version (BigEndian) 6411 assert(n.bitCast!(ubyte[4]) == [0xDE, 0xAD, 0xBE, 0xEF]); 6412 } 6413 6414 // Sizes must be compatible 6415 @safe unittest 6416 { 6417 uint n; 6418 6419 assert(!__traits(compiles, n.bitCast!ulong)); 6420 }