Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/trunk/tango/util/Convert.d

Revision 5344, 36.3 kB (checked in by DRK, 2 years ago)

Closes #1842. Thanks doob. Didn't use provided patch because it could do a full UTF conversion on a string just for the first character.

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