comparison tango/tango/util/Convert.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children
comparison
equal deleted inserted replaced
131:5825d48b27d1 132:1700239cab2e
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.Traits;
15 private import tango.core.Tuple : Tuple;
16 private import tango.core.Exception : TracedException;
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( DDoc )
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
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 D to(D,S)(S value);
135 D to(D,S)(S value, D default_); /// ditto
136 }
137 else
138 {
139 template to(D)
140 {
141 D to(S, Def=Missing)(S value, Def def=Def.init)
142 {
143 static if( is( Def == Missing ) )
144 return toImpl!(D,S)(value);
145
146 else
147 {
148 try
149 {
150 return toImpl!(D,S)(value);
151 }
152 catch( ConversionException e )
153 {}
154
155 return def;
156
157 //D result = def;
158 /+
159 try
160 {
161 return toImpl!(D,S)(value);
162 }
163 catch( ConversionException e )
164 {
165 return def;
166 }
167 // +/
168 //return result;
169 }
170 }
171 }
172 }
173
174 /**
175 * This exception is thrown when the to template is unable to perform a
176 * conversion at run-time. This typically occurs when the source value cannot
177 * be represented in the destination type. This exception is also thrown when
178 * the conversion would cause an over- or underflow.
179 */
180 class ConversionException : TracedException
181 {
182 this( char[] msg )
183 {
184 super( msg );
185 }
186 }
187
188 private:
189
190 typedef int Missing;
191
192 /*
193 * So, how is this module structured?
194 *
195 * Firstly, we need a bunch of support code. The first block of this contains
196 * some CTFE functions for string manipulation (to cut down on the number of
197 * template symbols we generate.)
198 *
199 * The next contains a boat-load of templates. Most of these are trait
200 * templates (things like isPOD, isObject, etc.) There are also a number of
201 * mixins, and some switching templates (like toString_(n).)
202 *
203 * Another thing to mention is intCmp, which performs a safe comparison
204 * between two integers of arbitrary size and signage.
205 *
206 * Following all this are the templated to* implementations.
207 *
208 * The actual toImpl template is the second last thing in the module, with the
209 * module unit tests coming last.
210 */
211
212 char ctfe_upper(char c)
213 {
214 if( 'a' <= c && c <= 'z' )
215 return (c - 'a') + 'A';
216 else
217 return c;
218 }
219
220 char[] ctfe_camelCase(char[] s)
221 {
222 char[] result;
223
224 bool nextIsCapital = true;
225
226 foreach( c ; s )
227 {
228 if( nextIsCapital )
229 {
230 if( c == '_' )
231 result ~= c;
232 else
233 {
234 result ~= ctfe_upper(c);
235 nextIsCapital = false;
236 }
237 }
238 else
239 {
240 if( c == '_' )
241 nextIsCapital = true;
242 else
243 result ~= c;
244 }
245 }
246
247 return result;
248 }
249
250 bool ctfe_isSpace(T)(T c)
251 {
252 static if (T.sizeof is 1)
253 return (c <= 32 && (c is ' ' | c is '\t' | c is '\r'
254 | c is '\n' | c is '\v' | c is '\f'));
255 else
256 return (c <= 32 && (c is ' ' | c is '\t' | c is '\r'
257 | c is '\n' | c is '\v' | c is '\f'))
258 || (c is '\u2028' | c is '\u2029');
259 }
260
261 T[] ctfe_triml(T)(T[] source)
262 {
263 if( source.length == 0 )
264 return null;
265
266 foreach( i,c ; source )
267 if( !ctfe_isSpace(c) )
268 return source[i..$];
269
270 return null;
271 }
272
273 T[] ctfe_trimr(T)(T[] source)
274 {
275 if( source.length == 0 )
276 return null;
277
278 foreach_reverse( i,c ; source )
279 if( !ctfe_isSpace(c) )
280 return source[0..i+1];
281
282 return null;
283 }
284
285 T[] ctfe_trim(T)(T[] source)
286 {
287 return ctfe_trimr(ctfe_triml(source));
288 }
289
290 template isPOD(T)
291 {
292 static if( is( T == struct ) || is( T == union ) )
293 const isPOD = true;
294 else
295 const isPOD = false;
296 }
297
298 template isObject(T)
299 {
300 static if( is( T == class ) || is( T == interface ) )
301 const isObject = true;
302 else
303 const isObject = false;
304 }
305
306 template isUDT(T)
307 {
308 const isUDT = isPOD!(T) || isObject!(T);
309 }
310
311 template isString(T)
312 {
313 static if( is( typeof(T[]) == char[] )
314 || is( typeof(T[]) == wchar[] )
315 || is( typeof(T[]) == dchar[] ) )
316 const isString = true;
317 else
318 const isString = false;
319 }
320
321 template isArrayType(T)
322 {
323 const isArrayType = isDynamicArrayType!(T) || isStaticArrayType!(T);
324 }
325
326 template isPointerType(T)
327 {
328 /*
329 * You might think these first two tests are redundant. You'd be wrong.
330 * The linux compilers, for whatever reason, seem to think that objects
331 * and arrays are implicitly castable to void*, whilst the Windows one
332 * doesn't. Don't ask me; just nod and smile...
333 */
334 static if( is( T : Object ) )
335 const isPointerType = false;
336 else static if( is( T : void[] ) )
337 const isPointerType = false;
338 else static if( is( T : void* ) )
339 const isPointerType = true;
340 else
341 const isPointerType = false;
342 }
343
344 static assert( isPointerType!(char*) );
345 static assert( isPointerType!(void*) );
346 static assert( !isPointerType!(char[]) );
347 static assert( !isPointerType!(void[]) );
348 static assert( !isPointerType!(typeof("abc")) );
349 static assert( !isPointerType!(Object) );
350
351 /*
352 * Determines which signed integer type of T and U is larger.
353 */
354 template sintSuperType(T,U)
355 {
356 static if( is( T == long ) || is( U == long ) )
357 alias long sintSuperType;
358 else static if( is( T == int ) || is( U == int ) )
359 alias int sintSuperType;
360 else static if( is( T == short ) || is( U == short ) )
361 alias short sintSuperType;
362 else static if( is( T == byte ) || is( U == byte ) )
363 alias byte sintSuperType;
364 }
365
366 /*
367 * Determines which unsigned integer type of T and U is larger.
368 */
369 template uintSuperType(T,U)
370 {
371 static if( is( T == ulong ) || is( U == ulong ) )
372 alias ulong sintSuperType;
373 else static if( is( T == uint ) || is( U == uint ) )
374 alias uint sintSuperType;
375 else static if( is( T == ushort ) || is( U == ushort ) )
376 alias ushort sintSuperType;
377 else static if( is( T == ubyte ) || is( U == ubyte ) )
378 alias ubyte sintSuperType;
379 }
380
381 template uintOfSize(uint bytes)
382 {
383 static if( bytes == 1 )
384 alias ubyte uintOfSize;
385 else static if( bytes == 2 )
386 alias ushort uintOfSize;
387 else static if( bytes == 4 )
388 alias uint uintOfSize;
389 }
390
391 /*
392 * Safely performs a comparison between two integer values, taking into
393 * account different sizes and signages.
394 */
395 int intCmp(T,U)(T lhs, U rhs)
396 {
397 static if( isSignedIntegerType!(T) && isSignedIntegerType!(U) )
398 {
399 alias sintSuperType!(T,U) S;
400 auto l = cast(S) lhs;
401 auto r = cast(S) rhs;
402 if( l < r ) return -1;
403 else if( l > r ) return 1;
404 else return 0;
405 }
406 else static if( isUnsignedIntegerType!(T) && isUnsignedIntegerType!(U) )
407 {
408 alias uintSuperType!(T,U) S;
409 auto l = cast(S) lhs;
410 auto r = cast(S) rhs;
411 if( l < r ) return -1;
412 else if( l > r ) return 1;
413 else return 0;
414 }
415 else
416 {
417 static if( isSignedIntegerType!(T) )
418 {
419 if( lhs < 0 )
420 return -1;
421 else
422 {
423 static if( U.sizeof >= T.sizeof )
424 {
425 auto l = cast(U) lhs;
426 if( l < rhs ) return -1;
427 else if( l > rhs ) return 1;
428 else return 0;
429 }
430 else
431 {
432 auto l = cast(ulong) lhs;
433 auto r = cast(ulong) rhs;
434 if( l < r ) return -1;
435 else if( l > r ) return 1;
436 else return 0;
437 }
438 }
439 }
440 else static if( isSignedIntegerType!(U) )
441 {
442 if( rhs < 0 )
443 return 1;
444 else
445 {
446 static if( T.sizeof >= U.sizeof )
447 {
448 auto r = cast(T) rhs;
449 if( lhs < r ) return -1;
450 else if( lhs > r ) return 1;
451 else return 0;
452 }
453 else
454 {
455 auto l = cast(ulong) lhs;
456 auto r = cast(ulong) rhs;
457 if( l < r ) return -1;
458 else if( l > r ) return 1;
459 else return 0;
460 }
461 }
462 }
463 }
464 }
465
466 template unsupported(char[] desc="")
467 {
468 static assert(false, "Unsupported conversion: cannot convert to "
469 ~ctfe_trim(D.stringof)~" from "
470 ~(desc!="" ? desc~" " : "")~ctfe_trim(S.stringof)~".");
471 }
472
473 template unsupported_backwards(char[] desc="")
474 {
475 static assert(false, "Unsupported conversion: cannot convert to "
476 ~(desc!="" ? desc~" " : "")~ctfe_trim(D.stringof)
477 ~" from "~ctfe_trim(S.stringof)~".");
478 }
479
480 // TN works out the c_case name of the given type.
481 template TN(T:T[])
482 {
483 static if( is( T == char ) )
484 const TN = "string";
485 else static if( is( T == wchar ) )
486 const TN = "wstring";
487 else static if( is( T == dchar ) )
488 const TN = "dstring";
489 else
490 const TN = TN!(T)~"_array";
491 }
492
493 // ditto
494 template TN(T:T*)
495 {
496 const TN = TN!(T)~"_pointer";
497 }
498
499 // ditto
500 template TN(T)
501 {
502 static if( isAssocArrayType!(T) )
503 const TN = TN!(typeof(T.keys[0]))~"_to_"
504 ~TN!(typeof(T.values[0]))~"_map";
505 else
506 const TN = ctfe_trim(T.stringof);
507 }
508
509 // Picks an appropriate toUtf* method from t.text.convert.Utf.
510 template toString_(T)
511 {
512 static if( is( T == char[] ) )
513 alias tango.text.convert.Utf.toString toString_;
514
515 else static if( is( T == wchar[] ) )
516 alias tango.text.convert.Utf.toString16 toString_;
517
518 else
519 alias tango.text.convert.Utf.toString32 toString_;
520 }
521
522 template UtfNum(T)
523 {
524 const UtfNum = is(typeof(T[0])==char) ? "8" : (
525 is(typeof(T[0])==wchar) ? "16" : "32");
526 }
527
528 template StringNum(T)
529 {
530 const StringNum = is(typeof(T[0])==char) ? "" : (
531 is(typeof(T[0])==wchar) ? "16" : "32");
532 }
533
534 // This mixin defines a general function for converting to a UDT.
535 template toUDT()
536 {
537 D toDfromS()
538 {
539 static if( isString!(S) )
540 {
541 static if( is( typeof(mixin("D.fromUtf"
542 ~UtfNum!(S)~"(value)")) : D ) )
543 return mixin("D.fromUtf"~UtfNum!(S)~"(value)");
544
545 else static if( is( typeof(D.fromUtf8(""c)) : D ) )
546 return D.fromUtf8(toString_!(char[])(value));
547
548 else static if( is( typeof(D.fromUtf16(""w)) : D ) )
549 return D.fromUtf16(toString_!(wchar[])(value));
550
551 else static if( is( typeof(D.fromUtf32(""d)) : D ) )
552 return D.fromUtf32(toString_!(dchar[])(value));
553
554 else static if( is( typeof(D.fromString(""c)) : D ) )
555 {
556 static if( is( S == char[] ) )
557 return D.fromString(value);
558
559 else
560 return D.fromString(toString_!(char[])(value));
561 }
562
563 // Default fallbacks
564
565 else static if( is( typeof(D.from!(S)(value)) : D ) )
566 return D.from!(S)(value);
567
568 else
569 mixin unsupported!("user-defined type");
570 }
571 else
572 {
573 // TODO: Check for templates. Dunno what to do about them.
574
575 static if( is( typeof(mixin("D.from_"~TN!(S)~"()")) : D ) )
576 return mixin("D.from_"~TN!(S)~"()");
577
578 else static if( is( typeof(mixin("D.from"
579 ~ctfe_camelCase(TN!(S))~"()")) : D ) )
580 return mixin("D.from"~ctfe_camelCase(TN!(S))~"()");
581
582 else static if( is( typeof(D.from!(S)(value)) : D ) )
583 return D.from!(S)(value);
584
585 else
586 mixin unsupported!("user-defined type");
587 }
588 }
589 }
590
591 // This mixin defines a general function for converting from a UDT.
592 template fromUDT(char[] fallthrough="")
593 {
594 D toDfromS()
595 {
596 static if( isString!(D) )
597 {
598 static if( is( typeof(mixin("value.toString"
599 ~StringNum!(D)~"()")) == D ) )
600 return mixin("value.toString"~StringNum!(D)~"()");
601
602 else static if( is( typeof(value.toString()) == char[] ) )
603 return toString_!(D)(value.toString);
604
605 else static if( is( typeof(value.toString16()) == wchar[] ) )
606 return toString_!(D)(value.toString16);
607
608 else static if( is( typeof(value.toString32()) == dchar[] ) )
609 return toString_!(D)(value.toString32);
610
611 else static if( is( typeof(value.toString()) == char[] ) )
612 {
613 static if( is( D == char[] ) )
614 return value.toString;
615
616 else
617 {
618 return toString_!(D)(value.toString);
619 }
620 }
621
622 // Default fallbacks
623
624 else static if( is( typeof(value.to!(D)()) : D ) )
625 return value.to!(D)();
626
627 else static if( fallthrough != "" )
628 mixin(fallthrough);
629
630 else
631 mixin unsupported!("user-defined type");
632 }
633 else
634 {
635 // TODO: Check for templates. Dunno what to do about them.
636
637 static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) )
638 return mixin("value.to_"~TN!(D)~"()");
639
640 else static if( is( typeof(mixin("value.to"
641 ~ctfe_camelCase(TN!(D))~"()")) : D ) )
642 return mixin("value.to"~ctfe_camelCase(TN!(D))~"()");
643
644 else static if( is( typeof(value.to!(D)()) : D ) )
645 return value.to!(D)();
646
647 else static if( fallthrough != "" )
648 mixin(fallthrough);
649
650 else
651 mixin unsupported!("user-defined type");
652 }
653 }
654 }
655
656 template convError()
657 {
658 void throwConvError()
659 {
660 // Since we're going to use to!(T) to convert the value to a string,
661 // we need to make sure we don't end up in a loop...
662 static if( isString!(D) || !is( typeof(to!(char[])(value)) == char[] ) )
663 {
664 throw new ConversionException("Could not convert a value of type "
665 ~S.stringof~" to type "~D.stringof~".");
666 }
667 else
668 {
669 throw new ConversionException("Could not convert `"
670 ~to!(char[])(value)~"` of type "
671 ~S.stringof~" to type "~D.stringof~".");
672 }
673 }
674 }
675
676 D toBool(D,S)(S value)
677 {
678 static assert(is(D==bool));
679
680 static if( isIntegerType!(S) /+|| isRealType!(S) || isImaginaryType!(S)
681 || isComplexType!(S)+/ )
682 // The weird comparison is to support NaN as true
683 return !(value == 0);
684
685 else static if( isCharType!(S) )
686 {
687 switch( value )
688 {
689 case 'F': case 'f':
690 return false;
691
692 case 'T': case 't':
693 return true;
694
695 default:
696 mixin convError;
697 throwConvError;
698 }
699 }
700
701 else static if( isString!(S) )
702 {
703 switch( Ascii.toLower(value) )
704 {
705 case "false":
706 return false;
707
708 case "true":
709 return true;
710
711 default:
712 mixin convError;
713 throwConvError;
714 }
715 }
716 /+
717 else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
718 {
719 mixin unsupported!("array type");
720 }
721 else static if( isAssocArrayType!(S) )
722 {
723 mixin unsupported!("associative array type");
724 }
725 else static if( isPointerType!(S) )
726 {
727 mixin unsupported!("pointer type");
728 }
729 else static if( is( S == typedef ) )
730 {
731 mixin unsupported!("typedef'ed type");
732 }
733 // +/
734 else static if( isPOD!(S) || isObject!(S) )
735 {
736 mixin fromUDT;
737 return toDfromS;
738 }
739 else
740 {
741 mixin unsupported;
742 }
743 }
744
745 D toIntegerFromInteger(D,S)(S value)
746 {
747 static if( (cast(ulong) D.max) >= (cast(ulong) S.max)
748 && (cast(long) D.min) <= (cast(long) S.min) )
749 {
750 return cast(D) value;
751 }
752 else
753 {
754 mixin convError; // TODO: Overflow error
755
756 if( intCmp(value,D.min)<0 || intCmp(value,D.max)>0 )
757 {
758 throwConvError;
759 }
760 else
761 return cast(D) value;
762 }
763 }
764
765 D toIntegerFromReal(D,S)(S value)
766 {
767 auto v = tango.math.Math.round(value);
768 if( (cast(real) D.min) <= v && v <= (cast(real) D.max) )
769 {
770 return cast(D) v;
771 }
772 else
773 {
774 mixin convError; // TODO: Overflow error
775 throwConvError;
776 }
777 }
778
779 D toIntegerFromString(D,S)(S value)
780 {
781 static if( is( S charT : charT[] ) )
782 {
783 mixin convError;
784
785 static if( is( D == ulong ) )
786 {
787 uint len;
788 auto result = tango.text.convert.Integer.convert(value, 10, &len);
789
790 if( len < value.length )
791 throwConvError;
792
793 return result;
794 }
795 else
796 {
797 uint len;
798 auto result = tango.text.convert.Integer.parse(value, 10, &len);
799
800 if( len < value.length )
801 throwConvError;
802
803 return toIntegerFromInteger!(D,long)(result);
804 }
805 }
806 }
807
808 D toInteger(D,S)(S value)
809 {
810 static if( is( S == bool ) )
811 return (value ? 1 : 0);
812
813 else static if( isIntegerType!(S) )
814 {
815 return toIntegerFromInteger!(D,S)(value);
816 }
817 else static if( isCharType!(S) )
818 {
819 if( value >= '0' && value <= '9' )
820 {
821 return cast(D)(value - '0');
822 }
823 else
824 {
825 mixin convError;
826 throwConvError;
827 }
828 }
829 else static if( isRealType!(S) )
830 {
831 return toIntegerFromReal!(D,S)(value);
832 }
833 else static if( isString!(S) )
834 {
835 return toIntegerFromString!(D,S)(value);
836 }
837 else static if( isPOD!(S) || isObject!(S) )
838 {
839 mixin fromUDT;
840 return toDfromS;
841 }
842 else
843 mixin unsupported;
844 }
845
846 D toReal(D,S)(S value)
847 {
848 /+static if( is( S == bool ) )
849 return (value ? 1.0 : 0.0);
850
851 else+/ static if( isIntegerType!(S) )
852 return cast(D) value;
853
854 /+else static if( isCharType!(S) )
855 return cast(D) to!(uint)(value);+/
856
857 else static if( isString!(S) )
858 return tango.text.convert.Float.parse(value);
859
860 else static if( isPOD!(S) || isObject!(S) )
861 {
862 mixin fromUDT;
863 return toDfromS;
864 }
865 else
866 mixin unsupported;
867 }
868
869 D toImaginary(D,S)(S value)
870 {
871 /+static if( is( S == bool ) )
872 return (value ? 1.0i : 0.0i);
873
874 else+/ static if( isComplexType!(S) )
875 {
876 if( value.re == 0.0 )
877 return value.im * cast(D)1.0i;
878
879 else
880 {
881 mixin convError;
882 throwConvError;
883 }
884 }
885 else static if( isPOD!(S) || isObject!(S) )
886 {
887 mixin fromUDT;
888 return toDfromS;
889 }
890 else
891 mixin unsupported;
892 }
893
894 D toComplex(D,S)(S value)
895 {
896 static if( isIntegerType!(S) || isRealType!(S) || isImaginaryType!(S)
897 || isComplexType!(S) )
898 return cast(D) value;
899
900 /+else static if( isCharType!(S) )
901 return cast(D) to!(uint)(value);+/
902
903 else static if( isPOD!(S) || isObject!(S) )
904 {
905 mixin fromUDT;
906 return toDfromS;
907 }
908 else
909 mixin unsupported;
910 }
911
912 D toChar(D,S)(S value)
913 {
914 static if( is( S == bool ) )
915 return (value ? 't' : 'f');
916
917 else static if( isIntegerType!(S) )
918 {
919 if( value >= 0 && value <= 9 )
920 return cast(D) value+'0';
921
922 else
923 {
924 mixin convError; // TODO: Overflow error
925 throwConvError;
926 }
927 }
928 else static if( isPOD!(S) || isObject!(S) )
929 {
930 mixin fromUDT;
931 return toDfromS;
932 }
933 else
934 mixin unsupported;
935 }
936
937 D toStringFromString(D,S)(S value)
938 {
939 static if( is( typeof(D[0]) == char ) )
940 return tango.text.convert.Utf.toString(value);
941
942 else static if( is( typeof(D[0]) == wchar ) )
943 return tango.text.convert.Utf.toString16(value);
944
945 else
946 {
947 static assert( is( typeof(D[0]) == dchar ) );
948 return tango.text.convert.Utf.toString32(value);
949 }
950 }
951
952 D toString(D,S)(S value)
953 {
954 static if( is( S == bool ) )
955 return (value ? "true" : "false");
956
957 else static if( isIntegerType!(S) )
958 // TODO: Make sure this works with ulongs.
959 return mixin("tango.text.convert.Integer.toString"~StringNum!(D)~"(value)");
960
961 else static if( isRealType!(S) )
962 return mixin("tango.text.convert.Float.toString"~StringNum!(D)~"(value)");
963
964 else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
965 mixin unsupported!("array type");
966
967 else static if( isAssocArrayType!(S) )
968 mixin unsupported!("associative array type");
969
970 else static if( isPOD!(S) || isObject!(S) )
971 {
972 mixin fromUDT;
973 return toDfromS;
974 }
975 else
976 mixin unsupported;
977 }
978
979 D fromString(D,S)(D value)
980 {
981 static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
982 mixin unsupported_backwards!("array type");
983
984 else static if( isAssocArrayType!(S) )
985 mixin unsupported_backwards!("associative array type");
986
987 else static if( isPOD!(S) || isObject!(S) )
988 {
989 mixin toUDT;
990 return toDfromS;
991 }
992 else
993 mixin unsupported_backwards;
994 }
995
996 D toArrayFromArray(D,S)(S value)
997 {
998 alias typeof(D[0]) De;
999
1000 D result; result.length = value.length;
1001 scope(failure) delete result;
1002
1003 foreach( i,e ; value )
1004 result[i] = to!(De)(e);
1005
1006 return result;
1007 }
1008
1009 D toMapFromMap(D,S)(S value)
1010 {
1011 alias typeof(D.keys[0]) Dk;
1012 alias typeof(D.values[0]) Dv;
1013
1014 D result;
1015
1016 foreach( k,v ; value )
1017 result[ to!(Dk)(k) ] = to!(Dv)(v);
1018
1019 return result;
1020 }
1021
1022 D toFromUDT(D,S)(S value)
1023 {
1024 // Try value.to* first
1025 static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) )
1026 return mixin("value.to_"~TN!(D)~"()");
1027
1028 else static if( is( typeof(mixin("value.to"
1029 ~ctfe_camelCase(TN!(D))~"()")) : D ) )
1030 return mixin("value.to"~ctfe_camelCase(TN!(D))~"()");
1031
1032 else static if( is( typeof(value.to!(D)()) : D ) )
1033 return value.to!(D)();
1034
1035 // Ok, try D.from* now
1036 else static if( is( typeof(mixin("D.from_"~TN!(S)~"(value)")) : D ) )
1037 return mixin("D.from_"~TN!(S)~"(value)");
1038
1039 else static if( is( typeof(mixin("D.from"
1040 ~ctfe_camelCase(TN!(S))~"(value)")) : D ) )
1041 return mixin("D.from"~ctfe_camelCase(TN!(S))~"(value)");
1042
1043 else static if( is( typeof(D.from!(S)(value)) : D ) )
1044 return D.from!(S)(value);
1045
1046 // Give up
1047 else
1048 mixin unsupported;
1049 }
1050
1051 D toImpl(D,S)(S value)
1052 {
1053 static if( is( D == S ) )
1054 return value;
1055
1056 else static if( isArrayType!(D) && isArrayType!(S)
1057 && is( typeof(D[0]) == typeof(S[0]) ) )
1058 // Special-case which catches to!(T[])!(T[n]).
1059 return value;
1060
1061 else static if( is( D == bool ) )
1062 return toBool!(D,S)(value);
1063
1064 else static if( isIntegerType!(D) )
1065 return toInteger!(D,S)(value);
1066
1067 else static if( isRealType!(D) )
1068 return toReal!(D,S)(value);
1069
1070 else static if( isImaginaryType!(D) )
1071 return toImaginary!(D,S)(value);
1072
1073 else static if( isComplexType!(D) )
1074 return toComplex!(D,S)(value);
1075
1076 else static if( isCharType!(D) )
1077 return toChar!(D,S)(value);
1078
1079 else static if( isString!(D) && isString!(S) )
1080 return toStringFromString!(D,S)(value);
1081
1082 else static if( isString!(D) )
1083 return toString!(D,S)(value);
1084
1085 else static if( isString!(S) )
1086 return fromString!(D,S)(value);
1087
1088 else static if( isArrayType!(D) && isArrayType!(S) )
1089 return toArrayFromArray!(D,S)(value);
1090
1091 else static if( isAssocArrayType!(D) && isAssocArrayType!(S) )
1092 return toMapFromMap!(D,S)(value);
1093
1094 else static if( isUDT!(D) || isUDT!(S) )
1095 return toFromUDT!(D,S)(value);
1096
1097 else
1098 mixin unsupported;
1099 }
1100
1101 debug ( ConvertTest ):
1102 void main() {}
1103
1104 debug( UnitTest ):
1105
1106
1107 bool ex(T)(lazy T v)
1108 {
1109 bool result = false;
1110 try
1111 {
1112 v();
1113 }
1114 catch( Exception _ )
1115 {
1116 result = true;
1117 }
1118 return result;
1119 }
1120
1121 bool nx(T)(lazy T v)
1122 {
1123 bool result = true;
1124 try
1125 {
1126 v();
1127 }
1128 catch( Exception _ )
1129 {
1130 result = false;
1131 }
1132 return result;
1133 }
1134
1135 struct Foo
1136 {
1137 int toInt() { return 42; }
1138
1139 char[] toString() { return "string foo"; }
1140
1141 int[] toIntArray() { return [1,2,3]; }
1142
1143 Bar toBar()
1144 {
1145 Bar result; return result;
1146 }
1147
1148 T to(T)()
1149 {
1150 static if( is( T == bool ) )
1151 return true;
1152 else
1153 static assert( false );
1154 }
1155 }
1156
1157 struct Bar
1158 {
1159 real toReal()
1160 {
1161 return 3.14159;
1162 }
1163
1164 ireal toIreal()
1165 {
1166 return 42.0i;
1167 }
1168 }
1169
1170 struct Baz
1171 {
1172 static Baz fromFoo(Foo foo)
1173 {
1174 Baz result; return result;
1175 }
1176
1177 Bar toBar()
1178 {
1179 Bar result; return result;
1180 }
1181 }
1182
1183 unittest
1184 {
1185 /*
1186 * bool
1187 */
1188 static assert( !is( typeof(to!(bool)(1.0)) ) );
1189 static assert( !is( typeof(to!(bool)(1.0i)) ) );
1190 static assert( !is( typeof(to!(bool)(1.0+1.0i)) ) );
1191
1192 assert( to!(bool)(0) == false );
1193 assert( to!(bool)(1) == true );
1194 assert( to!(bool)(-1) == true );
1195
1196 assert( to!(bool)('t') == true );
1197 assert( to!(bool)('T') == true );
1198 assert( to!(bool)('f') == false );
1199 assert( to!(bool)('F') == false );
1200 assert(ex( to!(bool)('x') ));
1201
1202 assert( to!(bool)("true") == true );
1203 assert( to!(bool)("false") == false );
1204 assert( to!(bool)("TrUe") == true );
1205 assert( to!(bool)("fAlSe") == false );
1206
1207 /*
1208 * Integer
1209 */
1210 assert( to!(int)(42L) == 42 );
1211 assert( to!(byte)(42) == cast(byte)42 );
1212 assert( to!(short)(-1701) == cast(short)-1701 );
1213 assert( to!(long)(cast(ubyte)72) == 72L );
1214
1215 assert(nx( to!(byte)(127) ));
1216 assert(ex( to!(byte)(128) ));
1217 assert(nx( to!(byte)(-128) ));
1218 assert(ex( to!(byte)(-129) ));
1219
1220 assert(nx( to!(ubyte)(255) ));
1221 assert(ex( to!(ubyte)(256) ));
1222 assert(nx( to!(ubyte)(0) ));
1223 assert(ex( to!(ubyte)(-1) ));
1224
1225 assert(nx( to!(long)(9_223_372_036_854_775_807UL) ));
1226 assert(ex( to!(long)(9_223_372_036_854_775_808UL) ));
1227 assert(nx( to!(ulong)(0L) ));
1228 assert(ex( to!(ulong)(-1L) ));
1229
1230 assert( to!(int)(3.14159) == 3 );
1231 assert( to!(int)(2.71828) == 3 );
1232
1233 assert( to!(int)("1234") == 1234 );
1234
1235 assert( to!(int)(true) == 1 );
1236 assert( to!(int)(false) == 0 );
1237
1238 assert( to!(int)('0') == 0 );
1239 assert( to!(int)('9') == 9 );
1240
1241 /*
1242 * Real
1243 */
1244 assert( to!(real)(3) == 3.0 );
1245 assert( to!(real)("1.125") == 1.125 );
1246
1247 /*
1248 * Imaginary
1249 */
1250 static assert( !is( typeof(to!(ireal)(3.0)) ) );
1251
1252 assert( to!(ireal)(0.0+1.0i) == 1.0i );
1253 assert(nx( to!(ireal)(0.0+1.0i) ));
1254 assert(ex( to!(ireal)(1.0+0.0i) ));
1255
1256 /*
1257 * Complex
1258 */
1259 assert( to!(creal)(1) == (1.0+0.0i) );
1260 assert( to!(creal)(2.0) == (2.0+0.0i) );
1261 assert( to!(creal)(3.0i) == (0.0+3.0i) );
1262
1263 /*
1264 * Char
1265 */
1266 assert( to!(char)(true) == 't' );
1267 assert( to!(char)(false) == 'f' );
1268
1269 assert( to!(char)(0) == '0' );
1270 assert( to!(char)(9) == '9' );
1271
1272 assert(ex( to!(char)(-1) ));
1273 assert(ex( to!(char)(10) ));
1274
1275 /*
1276 * String-string
1277 */
1278 assert( to!(char[])("Í love to æt "w) == "Í love to æt "c );
1279 assert( to!(char[])("them smûrƒies™,"d) == "them smûrƒies™,"c );
1280 assert( to!(wchar[])("Smûrfies™ I love"c) == "Smûrfies™ I love"w );
1281 assert( to!(wchar[])("2 食い散らす"d) == "2 食い散らす"w );
1282 assert( to!(dchar[])("bite đey µgly"c) == "bite đey µgly"d );
1283 assert( to!(dchar[])("headž ㍳ff"w) == "headž ㍳ff"d );
1284 // ... nibble on they bluish feet.
1285
1286 /*
1287 * String
1288 */
1289 assert( to!(char[])(true) == "true" );
1290 assert( to!(char[])(false) == "false" );
1291
1292 assert( to!(char[])(12345678) == "12345678" );
1293 assert( to!(char[])(1234.567800) == "1234.57");
1294
1295 /*
1296 * Array-array
1297 */
1298 assert( to!(ubyte[])([1,2,3]) == [cast(ubyte)1, 2, 3] );
1299 assert( to!(bool[])(["true"[], "false"]) == [true, false] );
1300
1301 /*
1302 * Map-map
1303 */
1304 {
1305 char[][int] src = [1:"true"[], 2:"false"];
1306 bool[ubyte] dst = to!(bool[ubyte])(src);
1307 assert( dst.keys.length == 2 );
1308 assert( dst[1] == true );
1309 assert( dst[2] == false );
1310 }
1311
1312 /*
1313 * UDT
1314 */
1315 {
1316 Foo foo;
1317
1318 assert( to!(bool)(foo) == true );
1319 assert( to!(int)(foo) == 42 );
1320 assert( to!(char[])(foo) == "string foo" );
1321 assert( to!(wchar[])(foo) == "string foo"w );
1322 assert( to!(dchar[])(foo) == "string foo"d );
1323 assert( to!(int[])(foo) == [1,2,3] );
1324 assert( to!(ireal)(to!(Bar)(foo)) == 42.0i );
1325 assert( to!(real)(to!(Bar)(to!(Baz)(foo))) == 3.14159 );
1326 }
1327
1328 /*
1329 * Default values
1330 */
1331 {
1332 assert( to!(int)("123", 456) == 123,
1333 `to!(int)("123", 456) == "` ~ to!(char[])(
1334 to!(int)("123", 456)) ~ `"` );
1335 assert( to!(int)("abc", 456) == 456,
1336 `to!(int)("abc", 456) == "` ~ to!(char[])(
1337 to!(int)("abc", 456)) ~ `"` );
1338 }
1339 }
1340