1 /** 2 * This module provides a templated function that performs value-preserving 3 * conversions between arbitrary types. This function's behaviour can be 4 * extended for user-defined types as needed. 5 * 6 * Copyright: Copyright © 2007 Daniel Keep. 7 * License: BSD style: $(LICENSE) 8 * Authors: Daniel Keep 9 * Credits: Inspired in part by Andrei Alexandrescu's work on std.conv. 10 */ 11 12 module tango.util.Convert; 13 14 private import tango.core.Exception; 15 private import tango.core.Traits; 16 private import tango.core.Tuple : Tuple; 17 18 private import tango.math.Math; 19 private import tango.text.convert.Utf; 20 private import tango.text.convert.Float; 21 private import tango.text.convert.Integer; 22 23 private import Ascii = tango.text.Ascii; 24 25 version( TangoDoc ) 26 { 27 /** 28 * Attempts to perform a value-preserving conversion of the given value 29 * from type S to type D. If the conversion cannot be performed in any 30 * context, a compile-time error will be issued describing the types 31 * involved. If the conversion fails at run-time because the destination 32 * type could not represent the value being converted, a 33 * ConversionException will be thrown. 34 * 35 * For example, to convert the string "123" into an equivalent integer 36 * value, you would use: 37 * 38 * ----- 39 * auto v = to!(int)("123"); 40 * ----- 41 * 42 * You may also specify a default value which should be returned in the 43 * event that the conversion cannot take place: 44 * 45 * ----- 46 * auto v = to!(int)("abc", 456); 47 * ----- 48 * 49 * The function will attempt to preserve the input value as exactly as 50 * possible, given the limitations of the destination format. For 51 * instance, converting a floating-point value to an integer will cause it 52 * to round the value to the nearest integer value. 53 * 54 * Below is a complete list of conversions between built-in types and 55 * strings. Capitalised names indicate classes of types. Conversions 56 * between types in the same class are also possible. 57 * 58 * ----- 59 * bool <-- Integer (0/!0), Char ('t'/'f'), String ("true"/"false") 60 * Integer <-- bool, Real, Char ('0'-'9'), String 61 * Real <-- Integer, String 62 * Imaginary <-- Complex 63 * Complex <-- Integer, Real, Imaginary 64 * Char <-- bool, Integer (0-9) 65 * String <-- bool, Integer, Real, Char 66 * ----- 67 * 68 * Conversions between arrays and associative arrays are also supported, 69 * and are done element-by-element. 70 * 71 * You can add support for value conversions to your types by defining 72 * appropriate static and instance member functions. Given a type 73 * the_type, any of the following members of a type T may be used: 74 * 75 * ----- 76 * the_type to_the_type(); 77 * static T from_the_type(the_type); 78 * ----- 79 * 80 * You may also use "camel case" names: 81 * 82 * ----- 83 * the_type toTheType(); 84 * static T fromTheType(the_type); 85 * ----- 86 * 87 * Arrays and associative arrays can also be explicitly supported: 88 * 89 * ----- 90 * the_type[] to_the_type_array(); 91 * the_type[] toTheTypeArray(); 92 * 93 * static T from_the_type_array(the_type[]); 94 * static T fromTheTypeArray(the_type[]); 95 * 96 * the_type[int] to_int_to_the_type_map(); 97 * the_type[int] toIntToTheTypeMap(); 98 * 99 * static T from_int_to_the_type_map(the_type[int]); 100 * static T fromIntToTheTypeMap(the_type[int]); 101 * ----- 102 * 103 * If you have more complex requirements, you can also use the generic to 104 * and from templated members: 105 * 106 * ----- 107 * the_type to(the_type)(); 108 * static T from(the_type)(the_type); 109 * ----- 110 * 111 * These templates will have the_type explicitly passed to them in the 112 * template instantiation. 113 * 114 * Finally, strings are given special support. The following members will 115 * be checked for: 116 * 117 * ----- 118 * char[] toString(); 119 * wchar[] toString16(); 120 * dchar[] toString32(); 121 * char[] toString(); 122 * ----- 123 * 124 * The "toString_" method corresponding to the destination string type will be 125 * tried first. If this method does not exist, then the function will 126 * look for another "toString_" method from which it will convert the result. 127 * Failing this, it will try "toString" and convert the result to the 128 * appropriate encoding. 129 * 130 * The rules for converting to a user-defined type are much the same, 131 * except it makes use of the "fromUtf8", "fromUtf16", "fromUtf32" and 132 * "fromString" static methods. 133 * 134 * Note: This module contains imports to other Tango modules that needs 135 * semantic analysis to be discovered. If your build tool doesn't do this 136 * properly, causing compile or link time problems, import the relevant 137 * module explicitly. 138 */ 139 D to(D,S)(S value); 140 D to(D,S)(S value, D default_); /// ditto 141 } 142 else 143 { 144 template to(D, S) 145 { 146 D to(S value) 147 { 148 return toImpl!(D, S)(value); 149 } 150 D to(S value, D fallback) 151 { 152 try 153 { 154 return toImpl!(D, S)(value); 155 } 156 catch (ConversionException e) 157 { 158 return fallback; 159 } 160 } 161 162 } 163 } 164 165 /** 166 * This exception is thrown when the to template is unable to perform a 167 * conversion at run-time. This typically occurs when the source value cannot 168 * be represented in the destination type. This exception is also thrown when 169 * the conversion would cause an over- or underflow. 170 */ 171 class ConversionException : Exception 172 { 173 this( immutable(char)[] msg ) 174 { 175 super( msg ); 176 } 177 } 178 179 private: 180 181 /* 182 * So, how is this module structured? 183 * 184 * Firstly, we need a bunch of support code. The first block of this contains 185 * some CTFE functions for string manipulation (to cut down on the number of 186 * template symbols we generate.) 187 * 188 * The next contains a boat-load of templates. Most of these are trait 189 * templates (things like isPOD, isObject, etc.) There are also a number of 190 * mixins, and some switching templates (like toString_(n).) 191 * 192 * Another thing to mention is intCmp, which performs a safe comparison 193 * between two integers of arbitrary size and signage. 194 * 195 * Following all this are the templated to* implementations. 196 * 197 * The actual toImpl template is the second last thing in the module, with the 198 * module unit tests coming last. 199 */ 200 201 char ctfe_upper(char c) 202 { 203 if( 'a' <= c && c <= 'z' ) 204 return cast(char)((c - 'a') + 'A'); 205 else 206 return c; 207 } 208 209 char[] ctfe_camelCase(const(char[]) s) 210 { 211 char[] result; 212 213 bool nextIsCapital = true; 214 215 foreach( c ; s ) 216 { 217 if( nextIsCapital ) 218 { 219 if( c == '_' ) 220 result ~= c; 221 else 222 { 223 result ~= ctfe_upper(c); 224 nextIsCapital = false; 225 } 226 } 227 else 228 { 229 if( c == '_' ) 230 nextIsCapital = true; 231 else 232 result ~= c; 233 } 234 } 235 236 return result; 237 } 238 239 bool ctfe_isSpace(T)(T c) 240 { 241 static if (T.sizeof is 1) 242 return (c <= 32 && (c is ' ' || c is '\t' || c is '\r' 243 || c is '\n' || c is '\v' || c is '\f')); 244 else 245 return (c <= 32 && (c is ' ' || c is '\t' || c is '\r' 246 || c is '\n' || c is '\v' || c is '\f')) 247 || (c is '\u2028' || c is '\u2029'); 248 } 249 250 T[] ctfe_triml(T)(T[] source) 251 { 252 if( source.length == 0 ) 253 return null; 254 255 foreach( i,c ; source ) 256 if( !ctfe_isSpace(c) ) 257 return source[i..$]; 258 259 return null; 260 } 261 262 T[] ctfe_trimr(T)(T[] source) 263 { 264 if( source.length == 0 ) 265 return null; 266 267 foreach_reverse( i,c ; source ) 268 if( !ctfe_isSpace(c) ) 269 return source[0..i+1]; 270 271 return null; 272 } 273 274 T[] ctfe_trim(T)(T[] source) 275 { 276 return ctfe_trimr(ctfe_triml(source)); 277 } 278 279 template isPOD(T) 280 { 281 static if( is( T == struct ) || is( T == union ) ) 282 enum isPOD = true; 283 else 284 enum isPOD = false; 285 } 286 287 template isObject(T) 288 { 289 static if( is( T == class ) || is( T == interface ) ) 290 enum isObject = true; 291 else 292 enum isObject = false; 293 } 294 295 template isUDT(T) 296 { 297 enum isUDT = isPOD!(T) || isObject!(T); 298 } 299 300 template isString(T) 301 { 302 static if( is( T : const(char[]) ) 303 || is( T : const(wchar[]) ) 304 || is( T : const(dchar[]) ) ) 305 enum isString = true; 306 else 307 enum isString = false; 308 } 309 310 static assert(isString!(string)); 311 312 template isMutableString(T) 313 { 314 static if( is( T == char[] ) 315 || is( T == wchar[] ) 316 || is( T == dchar[] ) ) 317 enum isMutableString = true; 318 else 319 enum isMutableString = false; 320 } 321 322 template isImmutableString(T) 323 { 324 static if( is( T == immutable(char)[] ) 325 || is( T == immutable(wchar)[] ) 326 || is( T == immutable(dchar)[] ) ) 327 enum isImmutableString = true; 328 else 329 enum isImmutableString = false; 330 } 331 332 template isArrayType(T) 333 { 334 enum isArrayType = isDynamicArrayType!(T) || isStaticArrayType!(T); 335 } 336 337 /* 338 * Determines which signed integer type of T and U is larger. 339 */ 340 template sintSuperType(T,U) 341 { 342 static if( is( T == long ) || is( U == long ) ) 343 alias long sintSuperType; 344 else static if( is( T == int ) || is( U == int ) ) 345 alias int sintSuperType; 346 else static if( is( T == short ) || is( U == short ) ) 347 alias short sintSuperType; 348 else static if( is( T == byte ) || is( U == byte ) ) 349 alias byte sintSuperType; 350 } 351 352 /* 353 * Determines which unsigned integer type of T and U is larger. 354 */ 355 template uintSuperType(T,U) 356 { 357 static if( is( T == ulong ) || is( U == ulong ) ) 358 alias ulong uintSuperType; 359 else static if( is( T == uint ) || is( U == uint ) ) 360 alias uint uintSuperType; 361 else static if( is( T == ushort ) || is( U == ushort ) ) 362 alias ushort uintSuperType; 363 else static if( is( T == ubyte ) || is( U == ubyte ) ) 364 alias ubyte uintSuperType; 365 } 366 367 template uintOfSize(uint bytes) 368 { 369 static if( bytes == 1 ) 370 alias ubyte uintOfSize; 371 else static if( bytes == 2 ) 372 alias ushort uintOfSize; 373 else static if( bytes == 4 ) 374 alias uint uintOfSize; 375 } 376 377 /* 378 * Safely performs a comparison between two integer values, taking into 379 * account different sizes and signages. 380 */ 381 int intCmp(T,U)(T lhs, U rhs) 382 { 383 static if( isSignedIntegerType!(T) && isSignedIntegerType!(U) ) 384 { 385 alias sintSuperType!(T,U) S; 386 auto l = cast(S) lhs; 387 auto r = cast(S) rhs; 388 if( l < r ) return -1; 389 else if( l > r ) return 1; 390 else return 0; 391 } 392 else static if( isUnsignedIntegerType!(T) && isUnsignedIntegerType!(U) ) 393 { 394 alias uintSuperType!(T,U) S; 395 auto l = cast(S) lhs; 396 auto r = cast(S) rhs; 397 if( l < r ) return -1; 398 else if( l > r ) return 1; 399 else return 0; 400 } 401 else 402 { 403 static if( isSignedIntegerType!(T) ) 404 { 405 if( lhs < 0 ) 406 return -1; 407 else 408 { 409 static if( U.sizeof >= T.sizeof ) 410 { 411 auto l = cast(U) lhs; 412 if( l < rhs ) return -1; 413 else if( l > rhs ) return 1; 414 else return 0; 415 } 416 else 417 { 418 auto l = cast(ulong) lhs; 419 auto r = cast(ulong) rhs; 420 if( l < r ) return -1; 421 else if( l > r ) return 1; 422 else return 0; 423 } 424 } 425 } 426 else static if( isSignedIntegerType!(U) ) 427 { 428 if( rhs < 0 ) 429 return 1; 430 else 431 { 432 static if( T.sizeof >= U.sizeof ) 433 { 434 auto r = cast(T) rhs; 435 if( lhs < r ) return -1; 436 else if( lhs > r ) return 1; 437 else return 0; 438 } 439 else 440 { 441 auto l = cast(ulong) lhs; 442 auto r = cast(ulong) rhs; 443 if( l < r ) return -1; 444 else if( l > r ) return 1; 445 else return 0; 446 } 447 } 448 } 449 } 450 } 451 452 template unsupported(immutable(char)[] desc="") 453 { 454 static assert(false, "Unsupported conversion: cannot convert to " 455 ~ctfe_trim(D.stringof)~" from " 456 ~(desc!="" ? desc~" " : "")~ctfe_trim(S.stringof)~"."); 457 } 458 459 template unsupported_backwards(immutable(char)[] desc="") 460 { 461 static assert(false, "Unsupported conversion: cannot convert to " 462 ~(desc!="" ? desc~" " : "")~ctfe_trim(D.stringof) 463 ~" from "~ctfe_trim(S.stringof)~"."); 464 } 465 466 // TN works out the c_case name of the given type. 467 template TN(T:const(T[])) 468 { 469 static if( is( T == char ) ) 470 enum TN = "string"; 471 else static if( is( T == wchar ) ) 472 enum TN = "wstring"; 473 else static if( is( T == dchar ) ) 474 enum TN = "dstring"; 475 else 476 enum TN = TN!(T)~"_array"; 477 } 478 479 // ditto 480 template TN(T:T*) 481 { 482 enum TN = TN!(T)~"_pointer"; 483 } 484 485 // ditto 486 template TN(T) 487 { 488 static if( isAssocArrayType!(T) ) 489 enum TN = TN!(typeof(T.keys[0]))~"_to_" 490 ~TN!(typeof(T.values[0]))~"_map"; 491 else 492 enum TN = ctfe_trim(T.stringof); 493 } 494 495 // Takes care of converting between mutable and immutable strings 496 D convertString_(D, C)(C[] ret) 497 { 498 static if(isImmutableString!(C)) 499 { 500 static if(isMutableString!(D)) 501 return cast(D) ret.dup; 502 else 503 return cast(D) ret; 504 } 505 else 506 { 507 static if(isImmutableString!(D)) 508 return cast(D) ret.idup; 509 else 510 return cast(D) ret; 511 } 512 } 513 514 // Picks an appropriate toString* method from t.text.convert.Utf. 515 T toString_(T, C)(C[] str) 516 { 517 static if( is( T : const(char[]) ) ) 518 return convertString_!(T)(tango.text.convert.Utf.toString(str)); 519 520 else static if( is( T : const(wchar[]) ) ) 521 return convertString_!(T)(tango.text.convert.Utf.toString16(str)); 522 523 else 524 return convertString_!(T)(tango.text.convert.Utf.toString32(str)); 525 } 526 527 template UtfNum(T) 528 { 529 enum UtfNum = is(ElementTypeOfArray!(T) : const(char)) ? "8" : ( 530 is(ElementTypeOfArray!(T) : const(wchar)) ? "16" : "32"); 531 } 532 533 template StringNum(T) 534 { 535 enum StringNum = is(ElementTypeOfArray!(T) : const(char)) ? "" : ( 536 is(ElementTypeOfArray!(T) : const(wchar)) ? "16" : "32"); 537 } 538 539 // Decodes a single dchar character from a string. Yes, I know they're 540 // actually code points, but I can't be bothered to type that much. Although 541 // I suppose I just typed MORE than that by writing this comment. Meh. 542 dchar firstCharOf(T)(T s, out size_t used) 543 { 544 static if( is( T : const(char[]) ) || is( T : const(wchar[]) ) ) 545 { 546 return tango.text.convert.Utf.decode(s, used); 547 } 548 else 549 { 550 used = 1; 551 return s[0]; 552 } 553 } 554 555 // This mixin defines a general function for converting to a UDT. 556 template toUDT() 557 { 558 D toDfromS() 559 { 560 static if( isString!(S) ) 561 { 562 static if( is( typeof(mixin("D.fromUtf" 563 ~UtfNum!(S)~"(value)")) : D ) ) 564 return mixin("D.fromUtf"~UtfNum!(S)~"(value)"); 565 566 else static if( is( typeof(D.fromUtf8(""c)) : D ) ) 567 return D.fromUtf8(toString_!(char[])(value)); 568 569 else static if( is( typeof(D.fromUtf16(""w)) : D ) ) 570 return D.fromUtf16(toString_!(wchar[])(value)); 571 572 else static if( is( typeof(D.fromUtf32(""d)) : D ) ) 573 return D.fromUtf32(toString_!(dchar[])(value)); 574 575 else static if( is( typeof(D.fromString(""c)) : D ) ) 576 { 577 static if( is( S == char[] ) ) 578 return D.fromString(value); 579 580 else 581 return D.fromString(toString_!(char[])(value)); 582 } 583 584 // Default fallbacks 585 586 else static if( is( typeof(D.from!(S)(value)) : D ) ) 587 return D.from!(S)(value); 588 589 else 590 mixin unsupported!("user-defined type"); 591 } 592 else 593 { 594 // TODO: Check for templates. Dunno what to do about them. 595 596 static if( is( typeof(mixin("D.from_"~TN!(S)~"()")) : D ) ) 597 return mixin("D.from_"~TN!(S)~"()"); 598 599 else static if( is( typeof(mixin("D.from" 600 ~ctfe_camelCase(TN!(S))~"()")) : D ) ) 601 return mixin("D.from"~ctfe_camelCase(TN!(S))~"()"); 602 603 else static if( is( typeof(D.from!(S)(value)) : D ) ) 604 return D.from!(S)(value); 605 606 else 607 mixin unsupported!("user-defined type"); 608 } 609 } 610 } 611 612 // This mixin defines a general function for converting from a UDT. 613 template fromUDT(immutable(char)[] fallthrough="") 614 { 615 D toDfromS() 616 { 617 static if( isString!(D) ) 618 { 619 static if( is( typeof(convertString_!(D)(mixin("value.toString" 620 ~StringNum!(D)~"()"))) : D ) ) 621 return convertString_!(D)(mixin("value.toString"~StringNum!(D)~"()")); 622 623 else static if( is( typeof(value.toString()) : const(char[]) ) ) 624 return toString_!(D)(value.toString()); 625 626 else static if( is( typeof(value.toString16()) : const(wchar[]) ) ) 627 return toString_!(D)(value.toString16); 628 629 else static if( is( typeof(value.toString32()) : const(dchar[]) ) ) 630 return toString_!(D)(value.toString32); 631 632 else static if( is( typeof(value.toString()) : const(char[]) ) ) 633 { 634 static if( is( D : const(char[]) ) ) 635 return value.toString(); 636 637 else 638 { 639 return toString_!(D)(value.toString()); 640 } 641 } 642 643 // Default fallbacks 644 645 else static if( is( typeof(value.to!(D)()) : D ) ) 646 return value.to!(D)(); 647 648 else static if( fallthrough != "" ) 649 mixin(fallthrough); 650 651 else 652 mixin unsupported!("user-defined type"); 653 } 654 else 655 { 656 // TODO: Check for templates. Dunno what to do about them. 657 658 static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) ) 659 return mixin("value.to_"~TN!(D)~"()"); 660 661 else static if( is( typeof(mixin("value.to" 662 ~ctfe_camelCase(TN!(D))~"()")) : D ) ) 663 return mixin("value.to"~ctfe_camelCase(TN!(D))~"()"); 664 665 else static if( is( typeof(value.to!(D)()) : D ) ) 666 return value.to!(D)(); 667 668 else static if( fallthrough != "" ) 669 mixin(fallthrough); 670 671 else 672 mixin unsupported!("user-defined type"); 673 } 674 } 675 } 676 677 template convError() 678 { 679 void throwConvError() 680 { 681 // Since we're going to use to!(T) to convert the value to a string, 682 // we need to make sure we don't end up in a loop... 683 static if( isString!(D) || !is( typeof(to!(immutable(char)[])(value)) == immutable(char)[] ) ) 684 { 685 throw new ConversionException("Could not convert a value of type " 686 ~S.stringof~" to type "~D.stringof~"."); 687 } 688 else 689 { 690 throw new ConversionException("Could not convert `" 691 ~to!(immutable(char)[])(value)~"` of type " 692 ~S.stringof~" to type "~D.stringof~"."); 693 } 694 } 695 } 696 697 D toBool(D,S)(S value) 698 { 699 static assert(is(D==bool)); 700 701 static if( isIntegerType!(S) /+|| isRealType!(S) || isImaginaryType!(S) 702 || isComplexType!(S)+/ ) 703 // The weird comparison is to support NaN as true 704 return !(value == 0); 705 706 else static if( isCharType!(S) ) 707 { 708 switch( value ) 709 { 710 case 'F': case 'f': 711 return false; 712 713 case 'T': case 't': 714 return true; 715 716 default: 717 mixin convError; 718 throwConvError(); 719 assert(0); 720 } 721 } 722 723 else static if( isString!(S) ) 724 { 725 if(value.length == 5 || value.length == 4) 726 { 727 char[5] buf; 728 buf[0..value.length] = value[]; 729 switch( Ascii.toLower(buf[0..value.length]) ) 730 { 731 case "false": 732 return false; 733 734 case "true": 735 return true; 736 737 default: 738 } 739 } 740 mixin convError; 741 throwConvError(); 742 assert(0); 743 } 744 /+ 745 else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) ) 746 { 747 mixin unsupported!("array type"); 748 } 749 else static if( isAssocArrayType!(S) ) 750 { 751 mixin unsupported!("associative array type"); 752 } 753 else static if( isPointerType!(S) ) 754 { 755 mixin unsupported!("pointer type"); 756 } 757 else static if( is( S == alias ) ) 758 { 759 mixin unsupported!("alias'ed type"); 760 } 761 // +/ 762 else static if( isPOD!(S) || isObject!(S) ) 763 { 764 mixin fromUDT; 765 return toDfromS(); 766 } 767 else 768 { 769 mixin unsupported; 770 } 771 } 772 773 D toIntegerFromInteger(D,S)(S value) 774 { 775 static if( (cast(ulong) D.max) < (cast(ulong) S.max) 776 || (cast(long) D.min) > (cast(long) S.min) ) 777 { 778 mixin convError; // TODO: Overflow error 779 780 if( intCmp(value,D.min)<0 || intCmp(value,D.max)>0 ) 781 { 782 throwConvError(); 783 } 784 } 785 return cast(D) value; 786 } 787 788 D toIntegerFromReal(D,S)(S value) 789 { 790 auto v = tango.math.Math.round(value); 791 if( (cast(real) D.min) <= v && v <= (cast(real) D.max) ) 792 { 793 return cast(D) v; 794 } 795 else 796 { 797 mixin convError; // TODO: Overflow error 798 throwConvError(); 799 assert(0); 800 } 801 } 802 803 D toIntegerFromString(D,S)(S value) 804 { 805 static if( is( S charT : charT[] ) ) 806 { 807 mixin convError; 808 809 static if( is( D == ulong ) ) 810 { 811 // Check for sign 812 S s = value; 813 814 if( s.length == 0 ) 815 throwConvError(); 816 817 else if( s[0] == '-' ) 818 throwConvError(); 819 820 else if( s[0] == '+' ) 821 s = s[1..$]; 822 823 size_t len; 824 auto result = tango.text.convert.Integer.convert(s, 10, &len); 825 826 if( len < s.length || len == 0 ) 827 throwConvError(); 828 829 return result; 830 } 831 else 832 { 833 size_t len; 834 auto result = tango.text.convert.Integer.parse(value, 10, &len); 835 836 if( len < value.length || len == 0 ) 837 throwConvError(); 838 839 return toIntegerFromInteger!(D,long)(result); 840 } 841 } 842 } 843 844 D toInteger(D,S)(S value) 845 { 846 static if( is( S == bool ) ) 847 return (value ? 1 : 0); 848 849 else static if( isIntegerType!(S) ) 850 { 851 return toIntegerFromInteger!(D,S)(value); 852 } 853 else static if( isCharType!(S) ) 854 { 855 if( value >= '0' && value <= '9' ) 856 { 857 return cast(D)(value - '0'); 858 } 859 else 860 { 861 mixin convError; 862 throwConvError(); 863 assert(0); 864 } 865 } 866 else static if( isRealType!(S) ) 867 { 868 return toIntegerFromReal!(D,S)(value); 869 } 870 else static if( isString!(S) ) 871 { 872 return toIntegerFromString!(D,S)(value); 873 } 874 else static if( isPOD!(S) || isObject!(S) ) 875 { 876 mixin fromUDT; 877 return toDfromS(); 878 } 879 else 880 mixin unsupported; 881 } 882 883 D toReal(D,S)(S value) 884 { 885 /+static if( is( S == bool ) ) 886 return (value ? 1.0 : 0.0); 887 888 else+/ static if( isIntegerType!(S) || isRealType!(S) ) 889 return cast(D) value; 890 891 /+else static if( isCharType!(S) ) 892 return cast(D) to!(uint)(value);+/ 893 894 else static if( isString!(S) ) 895 { 896 /+ 897 try 898 { 899 return tango.text.convert.Float.toFloat(value); 900 } 901 catch( IllegalArgumentException e ) 902 { 903 mixin convError; 904 throwConvError(); 905 } 906 +/ 907 908 mixin convError; 909 910 size_t len; 911 auto r = tango.text.convert.Float.parse(value, &len); 912 if( len < value.length || len == 0 ) 913 throwConvError(); 914 915 return r; 916 } 917 918 else static if( isPOD!(S) || isObject!(S) ) 919 { 920 mixin fromUDT; 921 return toDfromS(); 922 } 923 else 924 mixin unsupported; 925 } 926 927 D toImaginary(D,S)(S value) 928 { 929 /+static if( is( S == bool ) ) 930 return (value ? 1.0i : 0.0i); 931 932 else+/ static if( isComplexType!(S) ) 933 { 934 if( value.re == 0.0 ) 935 return value.im * cast(D)1.0i; 936 937 else 938 { 939 mixin convError; 940 throwConvError(); 941 assert(0); 942 } 943 } 944 else static if( isPOD!(S) || isObject!(S) ) 945 { 946 mixin fromUDT; 947 return toDfromS(); 948 } 949 else 950 mixin unsupported; 951 } 952 953 D toComplex(D,S)(S value) 954 { 955 static if( isIntegerType!(S) || isRealType!(S) || isImaginaryType!(S) 956 || isComplexType!(S) ) 957 return cast(D) value; 958 959 /+else static if( isCharType!(S) ) 960 return cast(D) to!(uint)(value);+/ 961 962 else static if( isPOD!(S) || isObject!(S) ) 963 { 964 mixin fromUDT; 965 return toDfromS(); 966 } 967 else 968 mixin unsupported; 969 } 970 971 D toChar(D,S)(S value) 972 { 973 static if( is( S == bool ) ) 974 return (value ? 't' : 'f'); 975 976 else static if( isIntegerType!(S) ) 977 { 978 if( value >= 0 && value <= 9 ) 979 return cast(D) (value+'0'); 980 981 else 982 { 983 mixin convError; // TODO: Overflow error 984 throwConvError(); 985 assert(0); 986 } 987 } 988 else static if( isString!(S) ) 989 { 990 void fail() 991 { 992 mixin convError; 993 throwConvError(); 994 } 995 996 if( value.length == 0 ) 997 { 998 fail(); 999 assert(0); 1000 } 1001 1002 else 1003 { 1004 size_t used; 1005 dchar c = firstCharOf(value, used); 1006 1007 if( used < value.length ) 1008 { 1009 fail(); // TODO: Overflow error 1010 assert(0); 1011 } 1012 1013 if( (cast(size_t) c) > (cast(size_t) D.max) ) 1014 { 1015 fail(); // TODO: Overflow error 1016 assert(0); 1017 } 1018 1019 return cast(D) c; 1020 } 1021 } 1022 else static if( isPOD!(S) || isObject!(S) ) 1023 { 1024 mixin fromUDT; 1025 return toDfromS(); 1026 } 1027 else 1028 mixin unsupported; 1029 } 1030 1031 D toStringFromString(D,S)(S value) 1032 { 1033 return toString_!(D)(value); 1034 } 1035 1036 __gshared immutable immutable(char)[] CHARS = 1037 "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ~ 1038 "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ~ 1039 "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ~ 1040 "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" ~ 1041 "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" ~ 1042 "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e"; 1043 1044 D toStringFromChar(D,S)(S value) 1045 { 1046 static if( is( D : S[] ) ) 1047 { 1048 static if( is( S == char ) ) 1049 { 1050 if( 0x20 <= value && value <= 0x7e ) 1051 { 1052 return convertString_!(D)((&CHARS[value-0x20])[0..1]); 1053 } 1054 } 1055 auto r = new S[1]; 1056 r[0] = value; 1057 return convertString_!(D)(r); 1058 } 1059 else 1060 { 1061 S[1] temp; 1062 temp[0] = value; 1063 return toStringFromString!(D,S[])(temp); 1064 } 1065 } 1066 1067 D toString(D,S)(S value) 1068 { 1069 static if( is( S == bool ) ) 1070 { 1071 return convertString_!(D)(value ? "true" : "false"); 1072 } 1073 else static if( isCharType!(S) ) 1074 return toStringFromChar!(D,S)(value); 1075 1076 else static if( isIntegerType!(S) ) 1077 { 1078 // TODO: Make sure this works with ulongs. 1079 return convertString_!(D)(mixin("tango.text.convert.Integer.toString"~StringNum!(D)~"(value)")); 1080 } 1081 1082 else static if( isRealType!(S) ) 1083 return convertString_!(D)(mixin("tango.text.convert.Float.toString"~StringNum!(D)~"(value)")); 1084 1085 else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) ) 1086 mixin unsupported!("array type"); 1087 1088 else static if( isAssocArrayType!(S) ) 1089 mixin unsupported!("associative array type"); 1090 1091 else static if( isPOD!(S) || isObject!(S) ) 1092 { 1093 mixin fromUDT; 1094 return toDfromS(); 1095 } 1096 else 1097 mixin unsupported; 1098 } 1099 1100 D fromString(D,S)(D value) 1101 { 1102 static if( isDynamicArrayType!(S) || isStaticArrayType!(S) ) 1103 mixin unsupported_backwards!("array type"); 1104 1105 else static if( isAssocArrayType!(S) ) 1106 mixin unsupported_backwards!("associative array type"); 1107 1108 else static if( isPOD!(S) || isObject!(S) ) 1109 { 1110 mixin toUDT; 1111 return toDfromS(); 1112 } 1113 else 1114 mixin unsupported_backwards; 1115 } 1116 1117 D toArrayFromArray(D,S)(S value) 1118 { 1119 alias BaseTypeOf!(ElementTypeOfArray!(D)) De; 1120 1121 De[] result; result.length = value.length; 1122 scope(failure) delete result; 1123 1124 foreach( i,e ; value ) 1125 result[i] = to!(De)(e); 1126 1127 /* Safe because it is newly allocated */ 1128 return cast(D)result; 1129 } 1130 1131 D toMapFromMap(D,S)(S value) 1132 { 1133 alias typeof(D.init.keys[0]) Dk; 1134 alias typeof(D.init.values[0]) Dv; 1135 1136 D result; 1137 1138 foreach( k,v ; value ) 1139 result[ to!(Dk)(k) ] = to!(Dv)(v); 1140 1141 return result; 1142 } 1143 1144 D toFromUDT(D,S)(S value) 1145 { 1146 // Try value.to* first 1147 static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) ) 1148 return mixin("value.to_"~TN!(D)~"()"); 1149 1150 else static if( is( typeof(mixin("value.to" 1151 ~ctfe_camelCase(TN!(D))~"()")) : D ) ) 1152 return mixin("value.to"~ctfe_camelCase(TN!(D))~"()"); 1153 1154 else static if( is( typeof(value.to!(D)()) : D ) ) 1155 return value.to!(D)(); 1156 1157 // Ok, try D.from* now 1158 else static if( is( typeof(mixin("D.from_"~TN!(S)~"(value)")) : D ) ) 1159 return mixin("D.from_"~TN!(S)~"(value)"); 1160 1161 else static if( is( typeof(mixin("D.from" 1162 ~ctfe_camelCase(TN!(S))~"(value)")) : D ) ) 1163 return mixin("D.from"~ctfe_camelCase(TN!(S))~"(value)"); 1164 1165 else static if( is( typeof(D.from!(S)(value)) : D ) ) 1166 return D.from!(S)(value); 1167 1168 // Give up 1169 else 1170 mixin unsupported; 1171 } 1172 1173 D toImpl(D,S)(S value) 1174 { 1175 static if( is( D == S ) ) 1176 return value; 1177 1178 else static if( is( S BaseType == enum ) ) 1179 return toImpl!(D,BaseType)(value); 1180 1181 else static if( isArrayType!(D) && isArrayType!(S) 1182 && is( typeof(D[0]) == typeof(S[0]) ) ) 1183 // Special-case which catches to!(T[])!(T[n]). 1184 return value; 1185 1186 else static if( is( D == bool ) ) 1187 return toBool!(D,S)(value); 1188 1189 else static if( isIntegerType!(D) ) 1190 return toInteger!(D,S)(value); 1191 1192 else static if( isRealType!(D) ) 1193 return toReal!(D,S)(value); 1194 1195 else static if( isImaginaryType!(D) ) 1196 return toImaginary!(D,S)(value); 1197 1198 else static if( isComplexType!(D) ) 1199 return toComplex!(D,S)(value); 1200 1201 else static if( isCharType!(D) ) 1202 return toChar!(D,S)(value); 1203 1204 else static if( isString!(D) && isString!(S) ) 1205 return toStringFromString!(D,S)(value); 1206 1207 else static if( isString!(D) ) 1208 return toString!(D,S)(value); 1209 1210 else static if( isString!(S) ) 1211 return fromString!(D,S)(value); 1212 1213 else static if( isArrayType!(D) && isArrayType!(S) ) 1214 return toArrayFromArray!(D,S)(value); 1215 1216 else static if( isAssocArrayType!(D) && isAssocArrayType!(S) ) 1217 return toMapFromMap!(D,S)(value); 1218 1219 else static if( isUDT!(D) || isUDT!(S) ) 1220 return toFromUDT!(D,S)(value); 1221 1222 else 1223 mixin unsupported; 1224 } 1225 1226 debug ( ConvertTest ) 1227 { 1228 void main() {} 1229 } 1230 1231 debug( UnitTest ): 1232 1233 1234 bool ex(T)(lazy T v) 1235 { 1236 bool result = false; 1237 try 1238 { 1239 v(); 1240 } 1241 catch( ConversionException _ ) 1242 { 1243 result = true; 1244 } 1245 return result; 1246 } 1247 1248 bool nx(T)(lazy T v) 1249 { 1250 bool result = true; 1251 try 1252 { 1253 v(); 1254 } 1255 catch( ConversionException _ ) 1256 { 1257 result = false; 1258 } 1259 return result; 1260 } 1261 1262 struct Foo 1263 { 1264 int toInt() { return 42; } 1265 1266 immutable(char)[] toString() { return "string foo"; } 1267 1268 int[] toIntArray() { return [1,2,3]; } 1269 1270 Bar toBar() 1271 { 1272 Bar result; return result; 1273 } 1274 1275 T to(T)() 1276 { 1277 static if( is( T == bool ) ) 1278 return true; 1279 else 1280 static assert( false ); 1281 } 1282 } 1283 1284 struct Bar 1285 { 1286 real toReal() 1287 { 1288 return 3.14159; 1289 } 1290 1291 ireal toIreal() 1292 { 1293 return 42.0i; 1294 } 1295 } 1296 1297 struct Baz 1298 { 1299 static Baz fromFoo(Foo foo) 1300 { 1301 Baz result; return result; 1302 } 1303 1304 Bar toBar() 1305 { 1306 Bar result; return result; 1307 } 1308 } 1309 1310 unittest 1311 { 1312 /* 1313 * bool 1314 */ 1315 static assert( !is( typeof(to!(bool)(1.0)) ) ); 1316 static assert( !is( typeof(to!(bool)(1.0i)) ) ); 1317 static assert( !is( typeof(to!(bool)(1.0+1.0i)) ) ); 1318 1319 assert( to!(bool)(0) == false ); 1320 assert( to!(bool)(1) == true ); 1321 assert( to!(bool)(-1) == true ); 1322 1323 assert( to!(bool)('t') == true ); 1324 assert( to!(bool)('T') == true ); 1325 assert( to!(bool)('f') == false ); 1326 assert( to!(bool)('F') == false ); 1327 assert(ex( to!(bool)('x') )); 1328 1329 assert( to!(bool)("true") == true ); 1330 assert( to!(bool)("false") == false ); 1331 assert( to!(bool)("TrUe") == true ); 1332 assert( to!(bool)("fAlSe") == false ); 1333 1334 /* 1335 * Integer 1336 */ 1337 assert( to!(int)(42L) == 42 ); 1338 assert( to!(byte)(42) == cast(byte)42 ); 1339 assert( to!(short)(-1701) == cast(short)-1701 ); 1340 assert( to!(long)(cast(ubyte)72) == 72L ); 1341 1342 assert(nx( to!(byte)(127) )); 1343 assert(ex( to!(byte)(128) )); 1344 assert(nx( to!(byte)(-128) )); 1345 assert(ex( to!(byte)(-129) )); 1346 1347 assert(nx( to!(ubyte)(255) )); 1348 assert(ex( to!(ubyte)(256) )); 1349 assert(nx( to!(ubyte)(0) )); 1350 assert(ex( to!(ubyte)(-1) )); 1351 1352 assert(nx( to!(long)(9_223_372_036_854_775_807UL) )); 1353 assert(ex( to!(long)(9_223_372_036_854_775_808UL) )); 1354 assert(nx( to!(ulong)(0L) )); 1355 assert(ex( to!(ulong)(-1L) )); 1356 1357 assert( to!(int)(3.14159) == 3 ); 1358 assert( to!(int)(2.71828) == 3 ); 1359 1360 assert( to!(int)("1234") == 1234 ); 1361 1362 assert( to!(int)(true) == 1 ); 1363 assert( to!(int)(false) == 0 ); 1364 1365 assert( to!(int)('0') == 0 ); 1366 assert( to!(int)('9') == 9 ); 1367 1368 /* 1369 * Real 1370 */ 1371 assert( to!(real)(3) == 3.0 ); 1372 assert( to!(real)("1.125") == 1.125 ); 1373 1374 /* 1375 * Imaginary 1376 */ 1377 static assert( !is( typeof(to!(ireal)(3.0)) ) ); 1378 1379 assert( to!(ireal)(0.0+1.0i) == 1.0i ); 1380 assert(nx( to!(ireal)(0.0+1.0i) )); 1381 assert(ex( to!(ireal)(1.0+0.0i) )); 1382 1383 /* 1384 * Complex 1385 */ 1386 assert( to!(creal)(1) == (1.0+0.0i) ); 1387 assert( to!(creal)(2.0) == (2.0+0.0i) ); 1388 assert( to!(creal)(3.0i) == (0.0+3.0i) ); 1389 1390 /* 1391 * Char 1392 */ 1393 assert( to!(char)(true) == 't' ); 1394 assert( to!(char)(false) == 'f' ); 1395 1396 assert( to!(char)(0) == '0' ); 1397 assert( to!(char)(9) == '9' ); 1398 1399 assert(ex( to!(char)(-1) )); 1400 assert(ex( to!(char)(10) )); 1401 1402 assert( to!(char)("a"d) == 'a' ); 1403 assert( to!(dchar)("ε"c) == 'ε' ); 1404 1405 assert(ex( to!(char)("ε"d) )); 1406 1407 /* 1408 * String-string 1409 */ 1410 assert( to!(char[])("Í love to æt "w) == "Í love to æt "c ); 1411 assert( to!(char[])("them smûrƒies™,"d) == "them smûrƒies™,"c ); 1412 assert( to!(wchar[])("Smûrfies™ I love"c) == "Smûrfies™ I love"w ); 1413 assert( to!(wchar[])("2 食い散らす"d) == "2 食い散らす"w ); 1414 assert( to!(dchar[])("bite đey µgly"c) == "bite đey µgly"d ); 1415 assert( to!(dchar[])("headž ㍳ff"w) == "headž ㍳ff"d ); 1416 // ... nibble on they bluish feet. 1417 1418 /* 1419 * String 1420 */ 1421 assert( to!(char[])(true) == "true" ); 1422 assert( to!(char[])(false) == "false" ); 1423 1424 assert( to!(char[])(12345678) == "12345678" ); 1425 assert( to!(char[])(1234.567800) == "1234.57"); 1426 1427 assert( to!( char[])(cast(char) 'a') == "a"c ); 1428 assert( to!(wchar[])(cast(char) 'b') == "b"w ); 1429 assert( to!(dchar[])(cast(char) 'c') == "c"d ); 1430 assert( to!( char[])(cast(wchar)'d') == "d"c ); 1431 assert( to!(wchar[])(cast(wchar)'e') == "e"w ); 1432 assert( to!(dchar[])(cast(wchar)'f') == "f"d ); 1433 assert( to!( char[])(cast(dchar)'g') == "g"c ); 1434 assert( to!(wchar[])(cast(dchar)'h') == "h"w ); 1435 assert( to!(dchar[])(cast(dchar)'i') == "i"d ); 1436 1437 /* 1438 * Array-array 1439 */ 1440 assert( to!(ubyte[])([1,2,3]) == [cast(ubyte)1, 2, 3] ); 1441 assert( to!(bool[])(["true"[], "false"]) == [true, false] ); 1442 1443 /* 1444 * Map-map 1445 */ 1446 { 1447 immutable(char)[][int] src = [1:"true"[], 2:"false"]; 1448 bool[ubyte] dst = to!(bool[ubyte])(src); 1449 assert( dst.keys.length == 2 ); 1450 assert( dst[1] == true ); 1451 assert( dst[2] == false ); 1452 } 1453 1454 /* 1455 * UDT 1456 */ 1457 { 1458 Foo foo; 1459 1460 assert( to!(bool)(foo) == true ); 1461 assert( to!(int)(foo) == 42 ); 1462 assert( to!(char[])(foo) == "string foo" ); 1463 assert( to!(wchar[])(foo) == "string foo"w ); 1464 assert( to!(dchar[])(foo) == "string foo"d ); 1465 assert( to!(int[])(foo) == [1,2,3] ); 1466 assert( to!(ireal)(to!(Bar)(foo)) == 42.0i ); 1467 assert( to!(real)(to!(Bar)(to!(Baz)(foo))) == 3.14159 ); 1468 } 1469 1470 /* 1471 * Default values 1472 */ 1473 { 1474 assert( to!(int)("123", 456) == 123, 1475 `to!(int)("123", 456) == "` ~ to!(char[])( 1476 to!(int)("123", 456)) ~ `"` ); 1477 assert( to!(int)("abc", 456) == 456, 1478 `to!(int)("abc", 456) == "` ~ to!(char[])( 1479 to!(int)("abc", 456)) ~ `"` ); 1480 } 1481 1482 /* 1483 * Ticket #1486 1484 */ 1485 { 1486 assert(ex( to!(int)("") )); 1487 1488 assert(ex( to!(real)("Foo") )); 1489 assert(ex( to!(real)("") )); 1490 assert(ex( to!(real)("0x1.2cp+9") )); 1491 1492 // From d0c's patch 1493 assert(ex( to!(int)("0x20") )); 1494 assert(ex( to!(int)("0x") )); 1495 assert(ex( to!(int)("-") )); 1496 assert(ex( to!(int)("-0x") )); 1497 1498 assert( to!(real)("0x20") == cast(real) 0x20 ); 1499 assert(ex( to!(real)("0x") )); 1500 assert(ex( to!(real)("-") )); 1501 } 1502 1503 /* 1504 * Const and immutable 1505 */ 1506 { 1507 assert( to!(immutable(char)[])("Í love to æt "w.dup) == "Í love to æt "c ); 1508 assert( to!(immutable(char)[])("them smûrƒies™,"d.dup) == "them smûrƒies™,"c ); 1509 assert( to!(immutable(wchar)[])("Smûrfies™ I love"c.dup) == "Smûrfies™ I love"w ); 1510 assert( to!(immutable(wchar)[])("2 食い散らす"d.dup) == "2 食い散らす"w ); 1511 assert( to!(immutable(dchar)[])("bite đey µgly"c.dup) == "bite đey µgly"d ); 1512 assert( to!(immutable(dchar)[])("headž ㍳ff"w.dup) == "headž ㍳ff"d ); 1513 // ... nibble on they bluish feet. 1514 1515 assert( to!(immutable(char)[])("食い散らす"c.dup) == "食い散らす"c ); 1516 assert( to!(immutable(wchar)[])("食い散らす"w.dup) == "食い散らす"w ); 1517 assert( to!(immutable(dchar)[])("食い散らす"d.dup) == "食い散らす"d ); 1518 1519 assert( to!(immutable(char)[])(true) == "true" ); 1520 assert( to!(immutable(char)[])(false) == "false" ); 1521 1522 assert( to!(immutable(char)[])(12345678) == "12345678" ); 1523 assert( to!(immutable(char)[])(1234.567800) == "1234.57"); 1524 1525 assert( to!(immutable( char)[])(cast(char) 'a') == "a"c ); 1526 assert( to!(immutable(wchar)[])(cast(char) 'b') == "b"w ); 1527 assert( to!(immutable(dchar)[])(cast(char) 'c') == "c"d ); 1528 assert( to!(immutable( char)[])(cast(wchar)'d') == "d"c ); 1529 assert( to!(immutable(wchar)[])(cast(wchar)'e') == "e"w ); 1530 assert( to!(immutable(dchar)[])(cast(wchar)'f') == "f"d ); 1531 assert( to!(immutable( char)[])(cast(dchar)'g') == "g"c ); 1532 assert( to!(immutable(wchar)[])(cast(dchar)'h') == "h"w ); 1533 assert( to!(immutable(dchar)[])(cast(dchar)'i') == "i"d ); 1534 1535 assert( to!(immutable(ubyte)[])([1,2,3]) == [cast(ubyte)1, 2, 3] ); 1536 1537 assert( to!(const(char)[])("Í love to æt "w) == "Í love to æt "c ); 1538 1539 Foo foo; 1540 1541 assert( to!(immutable(char)[])(foo) == "string foo" ); 1542 assert( to!(immutable(wchar)[])(foo) == "string foo"w ); 1543 assert( to!(immutable(dchar)[])(foo) == "string foo"d ); 1544 /* assert( to!(immutable(int)[])(foo) == [1,2,3] ); */ 1545 } 1546 1547 /* 1548 * Pass through 1549 */ 1550 { 1551 assert( to!(int)(cast(int)1) == 1 ); 1552 assert( to!(char[])("abc".dup) == "abc" ); 1553 assert( to!(immutable(char)[])("abc") == "abc" ); 1554 assert( to!(immutable(dchar)[])("abc"d) == "abc"d ); 1555 } 1556 } 1557