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 ireal toIreal()
1188 {
1189 return 42.0i;
1190 }
1191 }
1192
1193 struct Baz
1194 {
1195 static Baz fromFoo(Foo foo)
1196 {
1197 Baz result; return result;
1198 }
1199
1200 Bar toBar()
1201 {
1202 Bar result; return result;
1203 }
1204 }
1205 }
1206
1207 unittest
1208 {
1209 /*
1210 * bool
1211 */
1212 static assert( !is( typeof(to!(bool)(1.0)) ) );
1213 static assert( !is( typeof(to!(bool)(1.0i)) ) );
1214 static assert( !is( typeof(to!(bool)(1.0+1.0i)) ) );
1215
1216 test( to!(bool)(0) == false );
1217 test( to!(bool)(1) == true );
1218 test( to!(bool)(-1) == true );
1219
1220 test( to!(bool)('t') == true );
1221 test( to!(bool)('T') == true );
1222 test( to!(bool)('f') == false );
1223 test( to!(bool)('F') == false );
1224 test(ex( to!(bool)('x') ));
1225
1226 test( to!(bool)("true"[]) == true );
1227 test( to!(bool)("false"[]) == false );
1228 test( to!(bool)("TrUe"[]) == true );
1229 test( to!(bool)("fAlSe"[]) == false );
1230
1231 /*
1232 * Integer
1233 */
1234 test( to!(int)(42L) == 42 );
1235 test( to!(byte)(42) == cast(byte)42 );
1236 test( to!(short)(-1701) == cast(short)-1701 );
1237 test( to!(long)(cast(ubyte)72) == 72L );
1238
1239 test(nx( to!(byte)(127) ));
1240 test(ex( to!(byte)(128) ));
1241 test(nx( to!(byte)(-128) ));
1242 test(ex( to!(byte)(-129) ));
1243
1244 test(nx( to!(ubyte)(255) ));
1245 test(ex( to!(ubyte)(256) ));
1246 test(nx( to!(ubyte)(0) ));
1247 test(ex( to!(ubyte)(-1) ));
1248
1249 test(nx( to!(long)(9_223_372_036_854_775_807UL) ));
1250 test(ex( to!(long)(9_223_372_036_854_775_808UL) ));
1251 test(nx( to!(ulong)(0L) ));
1252 test(ex( to!(ulong)(-1L) ));
1253
1254 test( to!(int)(3.14159) == 3 );
1255 test( to!(int)(2.71828) == 3 );
1256
1257 test( to!(int)("1234"[]) == 1234 );
1258
1259 test( to!(int)(true) == 1 );
1260 test( to!(int)(false) == 0 );
1261
1262 test( to!(int)('0') == 0 );
1263 test( to!(int)('9') == 9 );
1264
1265 /*
1266 * Real
1267 */
1268 test( to!(real)(3) == 3.0 );
1269 test( to!(real)("1.125"[]) == 1.125 );
1270
1271 /*
1272 * Imaginary
1273 */
1274 static assert( !is( typeof(to!(ireal)(3.0)) ) );
1275
1276 test( to!(ireal)(0.0+1.0i) == 1.0i );
1277 test(nx( to!(ireal)(0.0+1.0i) ));
1278 test(ex( to!(ireal)(1.0+0.0i) ));
1279
1280 /*
1281 * Complex
1282 */
1283 test( to!(creal)(1) == (1.0+0.0i) );
1284 test( to!(creal)(2.0) == (2.0+0.0i) );
1285 test( to!(creal)(3.0i) == (0.0+3.0i) );
1286
1287 /*
1288 * Char
1289 */
1290 test( to!(char)(true) == 't' );
1291 test( to!(char)(false) == 'f' );
1292
1293 test( to!(char)(0) == '0' );
1294 test( to!(char)(9) == '9' );
1295
1296 test(ex( to!(char)(-1) ));
1297 test(ex( to!(char)(10) ));
1298
1299 test( to!(char)("a"d[]) == 'a' );
1300 test( to!(dchar)("ε"c[]) == 'ε' );
1301
1302 test(ex( to!(char)("ε"d[]) ));
1303
1304 /*
1305 * String-string
1306 */
1307 test( to!(char[])("Í love to æt "w[]) == "Í love to æt "c );
1308 test( to!(istring)("Í love to æt "w[]) == "Í love to æt "c );
1309 test( to!(char[])("them smûrƒies™,"d[]) == "them smûrƒies™,"c );
1310 test( to!(istring)("them smûrƒies™,"d[]) == "them smûrƒies™,"c );
1311 test( to!(wchar[])("Smûrfies™ I love"c[]) == "Smûrfies™ I love"w );
1312 test( to!(wchar[])("2 食い散らす"d[]) == "2 食い散らす"w );
1313 test( to!(dchar[])("bite đey µgly"c[]) == "bite đey µgly"d );
1314 test( to!(dchar[])("headž ㍳ff"w[]) == "headž ㍳ff"d );
1315 // ... nibble on they bluish feet.
1316
1317 /*
1318 * String
1319 */
1320 test( to!(char[])(true) == "true" );
1321 test( to!(istring)(true) == "true" );
1322 test( to!(char[])(false) == "false" );
1323
1324 test( to!(char[])(12345678) == "12345678" );
1325 test( to!(istring)(12345678) == "12345678" );
1326 test( to!(char[])(1234.567800) == "1234.57");
1327 test( to!(istring)(1234.567800) == "1234.57");
1328
1329 test( to!( char[])(cast(char) 'a') == "a"c );
1330 test( to!(wchar[])(cast(char) 'b') == "b"w );
1331 test( to!(dchar[])(cast(char) 'c') == "c"d );
1332 test( to!( char[])(cast(wchar)'d') == "d"c );
1333 test( to!(wchar[])(cast(wchar)'e') == "e"w );
1334 test( to!(dchar[])(cast(wchar)'f') == "f"d );
1335 test( to!( char[])(cast(dchar)'g') == "g"c );
1336 test( to!(wchar[])(cast(dchar)'h') == "h"w );
1337 test( to!(dchar[])(cast(dchar)'i') == "i"d );
1338
1339 /*
1340 * Array-array
1341 */
1342 test( to!(ubyte[])([1,2,3][]) == [cast(ubyte)1, 2, 3] );
1343 test( to!(bool[])(["true", "false"][]) == [true, false] );
1344
1345 /*
1346 * Map-map
1347 */
1348 {
1349 istring[int] src = [1:"true"[], 2:"false"];
1350 bool[ubyte] dst = to!(bool[ubyte])(src);
1351 test( dst.keys.length == 2 );
1352 test( dst[1] == true );
1353 test( dst[2] == false );
1354 }
1355
1356 /*
1357 * UDT
1358 */
1359 {
1360 Foo foo;
1361
1362 test( to!(bool)(foo) == true );
1363 test( to!(int)(foo) == 42 );
1364 test( to!(char[])(foo) == "string foo" );
1365 test( to!(wchar[])(foo) == "string foo"w );
1366 test( to!(dchar[])(foo) == "string foo"d );
1367 test( to!(int[])(foo) == [1,2,3] );
1368 test( to!(ireal)(to!(Bar)(foo)) == 42.0i );
1369 test( to!(real)(to!(Bar)(to!(Baz)(foo))) == 3.14159 );
1370 }
1371
1372 /*
1373 * Default values
1374 */
1375 {
1376 test( to!(int)("123"[], 456) == 123,
1377 `to!(int)("123", 456) == "` ~ to!(char[])(
1378 to!(int)("123"[], 456)) ~ `"` );
1379 test( to!(int)("abc"[], 456) == 456,
1380 `to!(int)("abc", 456) == "` ~ to!(char[])(
1381 to!(int)("abc"[], 456)) ~ `"` );
1382 }
1383
1384 /*
1385 * Ticket #1486
1386 */
1387 {
1388 test(ex( to!(int)(""[]) ));
1389
1390 test(ex( to!(real)("Foo"[]) ));
1391 test(ex( to!(real)(""[]) ));
1392 test(ex( to!(real)("0x1.2cp+9"[]) ));
1393
1394 // From d0c's patch
1395 test(ex( to!(int)("0x20"[]) ));
1396 test(ex( to!(int)("0x"[]) ));
1397 test(ex( to!(int)("-"[]) ));
1398 test(ex( to!(int)("-0x"[]) ));
1399
1400 test( to!(real)("0x20"[]) == cast(real) 0x20 );
1401 test(ex( to!(real)("0x"[]) ));
1402 test(ex( to!(real)("-"[]) ));
1403 }
1404 }
1405
1406 unittest
1407 {
1408 mixin(Typedef!(int, "MyInt"));
1409 MyInt value = 42;
1410 auto s = toImpl!(char[])(value);
1411 test (s == "42");
1412 }