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