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.transition;
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 (!isMutable!(DElem) || !isMutable!(SElem))
920 // both cases require creating new string which makes
921 // blindly casting result of transcoding to D inherently const correct
922 return cast(D) toStringFromString!(Unqual!(DElem)[])(value.dup);
923
924 else static if( is( DElem == char ) )
925 return ocean.text.convert.Utf.toString(value);
926
927 else static if( is( DElem == wchar ) )
928 return ocean.text.convert.Utf.toString16(value);
929
930 else
931 {
932 static assert( is( DElem == dchar ) );
933 return ocean.text.convert.Utf.toString32(value);
934 }
935 }
936
937 static immutable istring CHARS =
938 "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
939 ~ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
940 ~ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
941 ~ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
942 ~ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
943 ~ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e";
944
945 D toStringFromChar(D,S)(S value)
946 {
947 static if( is( D == S[] ) )
948 {
949 static if (is(S == Const!(char))
950 || is(S == Immut!(char)))
951 {
952 if( 0x20 <= value && value <= 0x7e )
953 return (&CHARS[value-0x20])[0..1];
954 }
955 auto r = new S[1];
956 r[0] = value;
957 return r;
958 }
959 else
960 {
961 S[1] temp;
962 temp[0] = value;
963 return toStringFromString!(D,S[])(temp);
964 }
965 }
966
967 D toString(D,S)(S value)
968 {
969 // casts to match const qualifier if any
970
971 static if( is( S == bool ) )
972 return (value ? to!(D)("true"[]) : to!(D)("false"[]));
973
974 else static if( isCharType!(S) )
975 return toStringFromChar!(D,S)(value);
976
977 else static if( isIntegerType!(S) )
978 // TODO: Make sure this works with ulongs.
979 return cast(D) mixin("ocean.text.convert.Integer_tango.toString"~StringNum!(D)~"(value)");
980
981 else static if( isRealType!(S) )
982 return cast(D) mixin("ocean.text.convert.Float.toString"~StringNum!(D)~"(value)");
983
984 else static if( isBasicArrayType!(S) )
985 mixin unsupported!("array type");
986
987 else static if( isArrayType!(S) == ArrayKind.Associative )
988 mixin unsupported!("associative array type");
989
990 else static if( isAggregateType!(S) )
991 {
992 mixin fromUDT;
993 return toDfromS;
994 }
995 else
996 mixin unsupported;
997 }
998
999 D fromString(D,S)(D value)
1000 {
1001 static if( isBasicArrayType!(S) )
1002 mixin unsupported_backwards!("array type");
1003
1004 else static if( isArrayType!(S) == ArrayKind.Associative )
1005 mixin unsupported_backwards!("associative array type");
1006
1007 else static if( isBasicArrayType!(S) )
1008 {
1009 mixin toUDT;
1010 return toDfromS;
1011 }
1012 else
1013 mixin unsupported_backwards;
1014 }
1015
1016 D toArrayFromArray(D,S)(S value)
1017 {
1018 alias ElementTypeOf!(D) De;
1019
1020 D result; result.length = value.length;
1021
1022 foreach( i,e ; value )
1023 result[i] = to!(De)(e);
1024
1025 return result;
1026 }
1027
1028 D toMapFromMap(D,S)(S value)
1029 {
1030 alias typeof(D.init.keys[0]) Dk;
1031 alias typeof(D.init.values[0]) Dv;
1032
1033 D result;
1034
1035 foreach( k,v ; value )
1036 result[ to!(Dk)(k) ] = to!(Dv)(v);
1037
1038 return result;
1039 }
1040
1041 D toFromUDT(D,S)(S value)
1042 {
1043 // D2 Typedef
1044 static if ( isTypedef!(S) == TypedefKind.Struct )
1045 return to!(D)(value.value);
1046 // Try value.to*
1047 else static if ( hasMember!(S, "to_" ~ TN!(D)) )
1048 return mixin("value.to_"~TN!(D)~"()");
1049 else static if ( hasMember!(S, "to" ~ ctfe_camelCase(TN!(D))) )
1050 return mixin("value.to"~ctfe_camelCase(TN!(D))~"()");
1051 else static if ( hasMember!(S, "to") && is(typeof(value.to!(D))) )
1052 return value.to!(D)();
1053 // Try D.from*
1054 else static if( hasMember!(D, "from_" ~ TN!(S)) )
1055 return mixin("D.from_"~TN!(S)~"(value)");
1056 else static if( hasMember!(D, "from" ~ ctfe_camelCase(TN!(S))) )
1057 return mixin("D.from"~ctfe_camelCase(TN!(S))~"(value)");
1058 else static if ( hasMember!(D, "from") && is(typeof(D.from!(S)(value))) )
1059 return D.from!(S)(value);
1060
1061 // Give up
1062 else
1063 mixin unsupported!();
1064 }
1065
1066 D toImpl(D,S)(S value)
1067 {
1068 static if( is( D == S ) )
1069 return value;
1070
1071 else static if ( isTypedef!(S) == TypedefKind.Keyword )
1072 return toImpl!(D,TypedefBaseType!(S))(value);
1073
1074 else static if ( isTypedef!(S) == TypedefKind.Struct )
1075 return toImpl!(D, typeof(S.value))(value.value);
1076
1077 else static if( is( S BaseType == enum ) )
1078 return toImpl!(D,BaseType)(value);
1079
1080 else static if( isBasicArrayType!(D) && isBasicArrayType!(S)
1081 && is( typeof(D[0]) == typeof(S[0]) ) )
1082 // Special-case which catches to!(T[])!(T[n]).
1083 return value;
1084
1085 else static if( is( D == bool ) )
1086 return toBool!(D,S)(value);
1087
1088 else static if( isIntegerType!(D) )
1089 return toInteger!(D,S)(value);
1090
1091 else static if( isRealType!(D) )
1092 return toReal!(D,S)(value);
1093
1094 else static if( isImaginaryType!(D) )
1095 return toImaginary!(D,S)(value);
1096
1097 else static if( isComplexType!(D) )
1098 return toComplex!(D,S)(value);
1099
1100 else static if( isCharType!(D) )
1101 return toChar!(D,S)(value);
1102
1103 else static if( isString!(D) && isString!(S) )
1104 return toStringFromString!(D,S)(value);
1105
1106 else static if( isString!(D) )
1107 return toString!(D,S)(value);
1108
1109 else static if( isString!(S) )
1110 return fromString!(D,S)(value);
1111
1112 else static if( isBasicArrayType!(D) && isBasicArrayType!(S) )
1113 return toArrayFromArray!(D,S)(value);
1114
1115 else static if( isArrayType!(D) == ArrayKind.Associative
1116 && isArrayType!(S) == ArrayKind.Associative )
1117 {
1118 return toMapFromMap!(D,S)(value);
1119 }
1120 else static if( isAggregateType!(D) || isAggregateType!(S) )
1121 return toFromUDT!(D,S)(value);
1122
1123 else
1124 mixin unsupported;
1125 }
1126
1127 version (UnitTest)
1128 {
1129 bool ex(T)(lazy T v)
1130 {
1131 bool result = false;
1132 try
1133 {
1134 v();
1135 }
1136 catch( ConversionException _ )
1137 {
1138 result = true;
1139 }
1140 return result;
1141 }
1142
1143 bool nx(T)(lazy T v)
1144 {
1145 bool result = true;
1146 try
1147 {
1148 v();
1149 }
1150 catch( ConversionException _ )
1151 {
1152 result = false;
1153 }
1154 return result;
1155 }
1156
1157 struct Foo
1158 {
1159 int toInt() { return 42; }
1160
1161 istring toString() { return "string foo"; }
1162
1163 int[] toIntArray() { return [1,2,3]; }
1164
1165 Bar toBar()
1166 {
1167 Bar result; return result;
1168 }
1169
1170 T to(T)()
1171 {
1172 static if( is( T == bool ) )
1173 return true;
1174 else
1175 static assert( false );
1176 }
1177 }
1178
1179 struct Bar
1180 {
1181 real toReal()
1182 {
1183 return 3.14159;
1184 }
1185
1186 ireal toIreal()
1187 {
1188 return 42.0i;
1189 }
1190 }
1191
1192 struct Baz
1193 {
1194 static Baz fromFoo(Foo foo)
1195 {
1196 Baz result; return result;
1197 }
1198
1199 Bar toBar()
1200 {
1201 Bar result; return result;
1202 }
1203 }
1204 }
1205
1206 unittest
1207 {
1208 /*
1209 * bool
1210 */
1211 static assert( !is( typeof(to!(bool)(1.0)) ) );
1212 static assert( !is( typeof(to!(bool)(1.0i)) ) );
1213 static assert( !is( typeof(to!(bool)(1.0+1.0i)) ) );
1214
1215 test( to!(bool)(0) == false );
1216 test( to!(bool)(1) == true );
1217 test( to!(bool)(-1) == true );
1218
1219 test( to!(bool)('t') == true );
1220 test( to!(bool)('T') == true );
1221 test( to!(bool)('f') == false );
1222 test( to!(bool)('F') == false );
1223 test(ex( to!(bool)('x') ));
1224
1225 test( to!(bool)("true"[]) == true );
1226 test( to!(bool)("false"[]) == false );
1227 test( to!(bool)("TrUe"[]) == true );
1228 test( to!(bool)("fAlSe"[]) == false );
1229
1230 /*
1231 * Integer
1232 */
1233 test( to!(int)(42L) == 42 );
1234 test( to!(byte)(42) == cast(byte)42 );
1235 test( to!(short)(-1701) == cast(short)-1701 );
1236 test( to!(long)(cast(ubyte)72) == 72L );
1237
1238 test(nx( to!(byte)(127) ));
1239 test(ex( to!(byte)(128) ));
1240 test(nx( to!(byte)(-128) ));
1241 test(ex( to!(byte)(-129) ));
1242
1243 test(nx( to!(ubyte)(255) ));
1244 test(ex( to!(ubyte)(256) ));
1245 test(nx( to!(ubyte)(0) ));
1246 test(ex( to!(ubyte)(-1) ));
1247
1248 test(nx( to!(long)(9_223_372_036_854_775_807UL) ));
1249 test(ex( to!(long)(9_223_372_036_854_775_808UL) ));
1250 test(nx( to!(ulong)(0L) ));
1251 test(ex( to!(ulong)(-1L) ));
1252
1253 test( to!(int)(3.14159) == 3 );
1254 test( to!(int)(2.71828) == 3 );
1255
1256 test( to!(int)("1234"[]) == 1234 );
1257
1258 test( to!(int)(true) == 1 );
1259 test( to!(int)(false) == 0 );
1260
1261 test( to!(int)('0') == 0 );
1262 test( to!(int)('9') == 9 );
1263
1264 /*
1265 * Real
1266 */
1267 test( to!(real)(3) == 3.0 );
1268 test( to!(real)("1.125"[]) == 1.125 );
1269
1270 /*
1271 * Imaginary
1272 */
1273 static assert( !is( typeof(to!(ireal)(3.0)) ) );
1274
1275 test( to!(ireal)(0.0+1.0i) == 1.0i );
1276 test(nx( to!(ireal)(0.0+1.0i) ));
1277 test(ex( to!(ireal)(1.0+0.0i) ));
1278
1279 /*
1280 * Complex
1281 */
1282 test( to!(creal)(1) == (1.0+0.0i) );
1283 test( to!(creal)(2.0) == (2.0+0.0i) );
1284 test( to!(creal)(3.0i) == (0.0+3.0i) );
1285
1286 /*
1287 * Char
1288 */
1289 test( to!(char)(true) == 't' );
1290 test( to!(char)(false) == 'f' );
1291
1292 test( to!(char)(0) == '0' );
1293 test( to!(char)(9) == '9' );
1294
1295 test(ex( to!(char)(-1) ));
1296 test(ex( to!(char)(10) ));
1297
1298 test( to!(char)("a"d[]) == 'a' );
1299 test( to!(dchar)("ε"c[]) == 'ε' );
1300
1301 test(ex( to!(char)("ε"d[]) ));
1302
1303 /*
1304 * String-string
1305 */
1306 test( to!(char[])("Í love to æt "w[]) == "Í love to æt "c );
1307 test( to!(istring)("Í love to æt "w[]) == "Í love to æt "c );
1308 test( to!(char[])("them smûrƒies™,"d[]) == "them smûrƒies™,"c );
1309 test( to!(istring)("them smûrƒies™,"d[]) == "them smûrƒies™,"c );
1310 test( to!(wchar[])("Smûrfies™ I love"c[]) == "Smûrfies™ I love"w );
1311 test( to!(wchar[])("2 食い散らす"d[]) == "2 食い散らす"w );
1312 test( to!(dchar[])("bite đey µgly"c[]) == "bite đey µgly"d );
1313 test( to!(dchar[])("headž ㍳ff"w[]) == "headž ㍳ff"d );
1314 // ... nibble on they bluish feet.
1315
1316 /*
1317 * String
1318 */
1319 test( to!(char[])(true) == "true" );
1320 test( to!(istring)(true) == "true" );
1321 test( to!(char[])(false) == "false" );
1322
1323 test( to!(char[])(12345678) == "12345678" );
1324 test( to!(istring)(12345678) == "12345678" );
1325 test( to!(char[])(1234.567800) == "1234.57");
1326 test( to!(istring)(1234.567800) == "1234.57");
1327
1328 test( to!( char[])(cast(char) 'a') == "a"c );
1329 test( to!(wchar[])(cast(char) 'b') == "b"w );
1330 test( to!(dchar[])(cast(char) 'c') == "c"d );
1331 test( to!( char[])(cast(wchar)'d') == "d"c );
1332 test( to!(wchar[])(cast(wchar)'e') == "e"w );
1333 test( to!(dchar[])(cast(wchar)'f') == "f"d );
1334 test( to!( char[])(cast(dchar)'g') == "g"c );
1335 test( to!(wchar[])(cast(dchar)'h') == "h"w );
1336 test( to!(dchar[])(cast(dchar)'i') == "i"d );
1337
1338 /*
1339 * Array-array
1340 */
1341 test( to!(ubyte[])([1,2,3][]) == [cast(ubyte)1, 2, 3] );
1342 test( to!(bool[])(["true", "false"][]) == [true, false] );
1343
1344 /*
1345 * Map-map
1346 */
1347 {
1348 istring[int] src = [1:"true"[], 2:"false"];
1349 bool[ubyte] dst = to!(bool[ubyte])(src);
1350 test( dst.keys.length == 2 );
1351 test( dst[1] == true );
1352 test( dst[2] == false );
1353 }
1354
1355 /*
1356 * UDT
1357 */
1358 {
1359 Foo foo;
1360
1361 test( to!(bool)(foo) == true );
1362 test( to!(int)(foo) == 42 );
1363 test( to!(char[])(foo) == "string foo" );
1364 test( to!(wchar[])(foo) == "string foo"w );
1365 test( to!(dchar[])(foo) == "string foo"d );
1366 test( to!(int[])(foo) == [1,2,3] );
1367 test( to!(ireal)(to!(Bar)(foo)) == 42.0i );
1368 test( to!(real)(to!(Bar)(to!(Baz)(foo))) == 3.14159 );
1369 }
1370
1371 /*
1372 * Default values
1373 */
1374 {
1375 test( to!(int)("123"[], 456) == 123,
1376 `to!(int)("123", 456) == "` ~ to!(char[])(
1377 to!(int)("123"[], 456)) ~ `"` );
1378 test( to!(int)("abc"[], 456) == 456,
1379 `to!(int)("abc", 456) == "` ~ to!(char[])(
1380 to!(int)("abc"[], 456)) ~ `"` );
1381 }
1382
1383 /*
1384 * Ticket #1486
1385 */
1386 {
1387 test(ex( to!(int)(""[]) ));
1388
1389 test(ex( to!(real)("Foo"[]) ));
1390 test(ex( to!(real)(""[]) ));
1391 test(ex( to!(real)("0x1.2cp+9"[]) ));
1392
1393 // From d0c's patch
1394 test(ex( to!(int)("0x20"[]) ));
1395 test(ex( to!(int)("0x"[]) ));
1396 test(ex( to!(int)("-"[]) ));
1397 test(ex( to!(int)("-0x"[]) ));
1398
1399 test( to!(real)("0x20"[]) == cast(real) 0x20 );
1400 test(ex( to!(real)("0x"[]) ));
1401 test(ex( to!(real)("-"[]) ));
1402 }
1403 }
1404
1405 unittest
1406 {
1407 mixin(Typedef!(int, "MyInt"));
1408 MyInt value = 42;
1409 auto s = toImpl!(char[])(value);
1410 test (s == "42");
1411 }