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