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 }