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