132
|
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
|