comparison tango/tango/core/Variant.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 * The variant module contains a variant, or polymorphic type.
3 *
4 * Copyright: Copyright (C) 2005-2007 The Tango Team. All rights reserved.
5 * License: BSD style: $(LICENSE)
6 * Authors: Daniel Keep, Sean Kelly
7 */
8 module tango.core.Variant;
9
10 private import tango.core.Exception : TracedException;
11 private import tango.core.Vararg : va_list;
12
13 private
14 {
15 template maxT(uint a, uint b)
16 {
17 const maxT = (a > b) ? a : b;
18 }
19
20 struct AtomicTypes
21 {
22 union
23 {
24 bool _bool;
25 char _char;
26 wchar _wchar;
27 dchar _dchar;
28 byte _byte;
29 short _short;
30 int _int;
31 long _long;
32 ubyte _ubyte;
33 ushort _ushort;
34 uint _uint;
35 ulong _ulong;
36 float _float;
37 double _double;
38 real _real;
39 ifloat _ifloat;
40 idouble _idouble;
41 ireal _ireal;
42 void* ptr;
43 void[] arr;
44 Object obj;
45 ubyte[maxT!(_real.sizeof,arr.sizeof)] data;
46 }
47 }
48
49 template isAtomicType(T)
50 {
51 static if( is( T == bool )
52 || is( T == char )
53 || is( T == wchar )
54 || is( T == dchar )
55 || is( T == byte )
56 || is( T == short )
57 || is( T == int )
58 || is( T == long )
59 || is( T == ubyte )
60 || is( T == ushort )
61 || is( T == uint )
62 || is( T == ulong )
63 || is( T == float )
64 || is( T == double )
65 || is( T == real )
66 || is( T == ifloat )
67 || is( T == idouble )
68 || is( T == ireal ) )
69 const isAtomicType = true;
70 else
71 const isAtomicType = false;
72 }
73
74 template isArray(T)
75 {
76 static if( is( T U : U[] ) )
77 const isArray = true;
78 else
79 const isArray = false;
80 }
81
82 template isPointer(T)
83 {
84 static if( is( T U : U* ) )
85 const isPointer = true;
86 else
87 const isPointer = false;
88 }
89
90 template isObject(T)
91 {
92 static if( is( T : Object ) )
93 const isObject = true;
94 else
95 const isObject = false;
96 }
97
98 template isStaticArray(T)
99 {
100 static if( is( typeof(T.init)[(T).sizeof / typeof(T.init).sizeof] == T ) )
101 const isStaticArray = true;
102 else
103 const isStaticArray = false;
104 }
105
106 bool isAny(T,argsT...)(T v, argsT args)
107 {
108 foreach( arg ; args )
109 if( v is arg ) return true;
110 return false;
111 }
112
113 const tibool = typeid(bool);
114 const tichar = typeid(char);
115 const tiwchar = typeid(wchar);
116 const tidchar = typeid(dchar);
117 const tibyte = typeid(byte);
118 const tishort = typeid(short);
119 const tiint = typeid(int);
120 const tilong = typeid(long);
121 const tiubyte = typeid(ubyte);
122 const tiushort = typeid(ushort);
123 const tiuint = typeid(uint);
124 const tiulong = typeid(ulong);
125 const tifloat = typeid(float);
126 const tidouble = typeid(double);
127 const tireal = typeid(real);
128 const tiifloat = typeid(ifloat);
129 const tiidouble = typeid(idouble);
130 const tiireal = typeid(ireal);
131
132 bool canImplicitCastTo(dsttypeT)(TypeInfo srctype)
133 {
134 static if( is( dsttypeT == char ) )
135 return isAny(srctype, tibool, tiubyte);
136
137 else static if( is( dsttypeT == wchar ) )
138 return isAny(srctype, tibool, tiubyte, tiushort, tichar);
139
140 else static if( is( dsttypeT == dchar ) )
141 return isAny(srctype, tibool, tiubyte, tiushort, tiuint, tichar,
142 tiwchar);
143
144 else static if( is( dsttypeT == byte ) )
145 return isAny(srctype, tibool);
146
147 else static if( is( dsttypeT == ubyte ) )
148 return isAny(srctype, tibool, tichar);
149
150 else static if( is( dsttypeT == short ) )
151 return isAny(srctype, tibool, tibyte, tiubyte, tichar);
152
153 else static if( is( dsttypeT == ushort ) )
154 return isAny(srctype, tibool, tibyte, tiubyte, tichar, tiwchar);
155
156 else static if( is( dsttypeT == int ) )
157 return isAny(srctype, tibool, tibyte, tiubyte, tishort, tiushort,
158 tichar, tiwchar);
159
160 else static if( is( dsttypeT == uint ) )
161 return isAny(srctype, tibool, tibyte, tiubyte, tishort, tiushort,
162 tichar, tiwchar, tidchar);
163
164 else static if( is( dsttypeT == long ) || is( dsttypeT == ulong ) )
165 return isAny(srctype, tibool, tibyte, tiubyte, tishort, tiushort,
166 tiint, tiuint, tichar, tiwchar, tidchar);
167
168 else static if( is( dsttypeT == float ) )
169 return isAny(srctype, tibool, tibyte, tiubyte);
170
171 else static if( is( dsttypeT == double ) )
172 return isAny(srctype, tibool, tibyte, tiubyte, tifloat);
173
174 else static if( is( dsttypeT == real ) )
175 return isAny(srctype, tibool, tibyte, tiubyte, tifloat, tidouble);
176
177 else static if( is( dsttypeT == idouble ) )
178 return isAny(srctype, tiifloat);
179
180 else static if( is( dsttypeT == ireal ) )
181 return isAny(srctype, tiifloat, tiidouble);
182
183 else
184 return false;
185 }
186
187 template storageT(T)
188 {
189 static if( isStaticArray!(T) )
190 alias typeof(T.dup) storageT;
191 else
192 alias T storageT;
193 }
194 }
195
196 /**
197 * This exception is thrown whenever you attempt to get the value of a Variant
198 * without using a compatible type.
199 */
200 class VariantTypeMismatchException : TracedException
201 {
202 this(TypeInfo expected, TypeInfo got)
203 {
204 super("cannot convert "~expected.toString
205 ~" value to a "~got.toString);
206 }
207 }
208
209 /**
210 * The Variant type is used to dynamically store values of different types at
211 * runtime.
212 *
213 * You can create a Variant using either the pseudo-constructor or direct
214 * assignment.
215 *
216 * -----
217 * Variant v = Variant(42);
218 * v = "abc";
219 * -----
220 */
221 struct Variant
222 {
223 /**
224 * This pseudo-constructor is used to place a value into a new Variant.
225 *
226 * Params:
227 * value = The value you wish to put in the Variant.
228 *
229 * Returns:
230 * The new Variant.
231 */
232 static Variant opCall(T)(T value)
233 {
234 Variant _this;
235
236 static if( isStaticArray!(T) )
237 _this = value.dup;
238
239 else
240 _this = value;
241
242 return _this;
243 }
244
245 /**
246 * This operator allows you to assign arbitrary values directly into an
247 * existing Variant.
248 *
249 * Params:
250 * value = The value you wish to put in the Variant.
251 *
252 * Returns:
253 * The new value of the assigned-to variant.
254 */
255 Variant opAssign(T)(T value)
256 {
257 static if( isStaticArray!(T) )
258 {
259 return (*this = value.dup);
260 }
261 else
262 {
263 type = typeid(T);
264
265 static if( isAtomicType!(T) )
266 {
267 mixin("this.value._"~T.stringof~"=value;");
268 }
269 else static if( isArray!(T) )
270 {
271 this.value.arr = (cast(void*)value.ptr)
272 [0 .. value.length];
273 }
274 else static if( isPointer!(T) )
275 {
276 this.value.ptr = cast(void*)T;
277 }
278 else static if( isObject!(T) )
279 {
280 this.value.obj = T;
281 }
282 else
283 {
284 if( T.sizeof <= this.value.data.length )
285 {
286 this.value.data[0..T.sizeof] =
287 (cast(ubyte*)&value)[0..T.sizeof];
288 }
289 else
290 {
291 auto buffer = (cast(ubyte*)&value)[0..T.sizeof].dup;
292 this.value.arr = cast(void[])buffer;
293 }
294 }
295 return *this;
296 }
297 }
298
299 /**
300 * This member can be used to determine if the value stored in the Variant
301 * is of the specified type. Note that this comparison is exact: it does
302 * not take implicit casting rules into account.
303 *
304 * Returns:
305 * true if the Variant contains a value of type T, false otherwise.
306 */
307 bool isA(T)()
308 {
309 return cast(bool)(typeid(T) is type);
310 }
311
312 /**
313 * This member can be used to determine if the value stored in the Variant
314 * is of the specified type. This comparison attempts to take implicit
315 * conversion rules into account.
316 *
317 * Returns:
318 * true if the Variant contains a value of type T, or if the Variant
319 * contains a value that can be implicitly cast to type T; false
320 * otherwise.
321 */
322 bool isImplicitly(T)()
323 {
324 return ( cast(bool)(typeid(T) is type)
325 || canImplicitCastTo!(T)(type) );
326 }
327
328 /**
329 * This determines whether the Variant has an assigned value or not. It
330 * is simply short-hand for calling the isA member with a type of void.
331 *
332 * Returns:
333 * true if the Variant does not contain a value, false otherwise.
334 */
335 bool isEmpty()
336 {
337 return isA!(void);
338 }
339
340 /**
341 * This member will clear the Variant, returning it to an empty state.
342 */
343 void clear()
344 {
345 _type = typeid(void);
346 value = value.init;
347 }
348
349 /**
350 * This is the primary mechanism for extracting a value from a Variant.
351 * Given a destination type S, it will attempt to extract the value of the
352 * Variant into that type. If the value contained within the Variant
353 * cannot be implicitly cast to the given type S, it will throw an
354 * exception.
355 *
356 * You can check to see if this operation will fail by calling the
357 * isImplicitly member with the type S.
358 *
359 * Returns:
360 * The value stored within the Variant.
361 */
362 storageT!(S) get(S)()
363 {
364 alias storageT!(S) T;
365
366 if( type !is typeid(T)
367 // Let D do runtime check itself
368 && !isObject!(T)
369 // Allow implicit upcasts
370 && !canImplicitCastTo!(T)(type)
371 )
372 throw new VariantTypeMismatchException(type,typeid(T));
373
374 static if( isAtomicType!(T) )
375 {
376 if( type is typeid(T) )
377 {
378 return mixin("this.value._"~T.stringof);
379 }
380 else
381 {
382 if( type is tibool ) return cast(T)this.value._bool;
383 else if( type is tichar ) return cast(T)this.value._char;
384 else if( type is tiwchar ) return cast(T)this.value._wchar;
385 else if( type is tidchar ) return cast(T)this.value._dchar;
386 else if( type is tibyte ) return cast(T)this.value._byte;
387 else if( type is tishort ) return cast(T)this.value._short;
388 else if( type is tiint ) return cast(T)this.value._int;
389 else if( type is tilong ) return cast(T)this.value._long;
390 else if( type is tiubyte ) return cast(T)this.value._ubyte;
391 else if( type is tiushort ) return cast(T)this.value._ushort;
392 else if( type is tiuint ) return cast(T)this.value._uint;
393 else if( type is tiulong ) return cast(T)this.value._ulong;
394 else if( type is tifloat ) return cast(T)this.value._float;
395 else if( type is tidouble ) return cast(T)this.value._double;
396 else if( type is tireal ) return cast(T)this.value._real;
397 else if( type is tiifloat ) return cast(T)this.value._ifloat;
398 else if( type is tiidouble ) return cast(T)this.value._idouble;
399 else if( type is tiireal ) return cast(T)this.value._ireal;
400 else
401 throw new VariantTypeMismatchException(type,typeid(T));
402 }
403 }
404 else static if( isArray!(T) )
405 {
406 return (cast(typeof(T[0])*)this.value.arr.ptr)
407 [0 .. this.value.arr.length];
408 }
409 else static if( isPointer!(T) )
410 {
411 return cast(T)this.value.ptr;
412 }
413 else static if( isObject!(T) )
414 {
415 return cast(T)this.value.obj;
416 }
417 else
418 {
419 if( T.sizeof <= this.value.data.length )
420 {
421 T result;
422 (cast(ubyte*)&result)[0..T.sizeof] =
423 this.value.data[0..T.sizeof];
424 return result;
425 }
426 else
427 {
428 T result;
429 (cast(ubyte*)&result)[0..T.sizeof] =
430 (cast(ubyte[])this.value.arr)[0..T.sizeof];
431 return result;
432 }
433 }
434 assert(false);
435 }
436
437 /**
438 * The following operator overloads are defined for the sake of
439 * convenience. It is important to understand that they do not allow you
440 * to use a Variant as both the left-hand and right-hand sides of an
441 * expression. One side of the operator must be a concrete type in order
442 * for the Variant to know what code to generate.
443 */
444 typeof(T+T) opAdd(T)(T rhs) { return get!(T) + rhs; }
445 typeof(T+T) opAdd_r(T)(T lhs) { return lhs + get!(T); } /// ditto
446 typeof(T-T) opSub(T)(T rhs) { return get!(T) - rhs; } /// ditto
447 typeof(T-T) opSub_r(T)(T lhs) { return lhs - get!(T); } /// ditto
448 typeof(T*T) opMul(T)(T rhs) { return get!(T) * rhs; } /// ditto
449 typeof(T*T) opMul_r(T)(T lhs) { return lhs * get!(T); } /// ditto
450 typeof(T/T) opDiv(T)(T rhs) { return get!(T) / rhs; } /// ditto
451 typeof(T/T) opDiv_r(T)(T lhs) { return lhs / get!(T); } /// ditto
452 typeof(T%T) opMod(T)(T rhs) { return get!(T) % rhs; } /// ditto
453 typeof(T%T) opMod_r(T)(T lhs) { return lhs % get!(T); } /// ditto
454 typeof(T&T) opAnd(T)(T rhs) { return get!(T) & rhs; } /// ditto
455 typeof(T&T) opAnd_r(T)(T lhs) { return lhs & get!(T); } /// ditto
456 typeof(T|T) opOr(T)(T rhs) { return get!(T) | rhs; } /// ditto
457 typeof(T|T) opOr_r(T)(T lhs) { return lhs | get!(T); } /// ditto
458 typeof(T^T) opXor(T)(T rhs) { return get!(T) ^ rhs; } /// ditto
459 typeof(T^T) opXor_r(T)(T lhs) { return lhs ^ get!(T); } /// ditto
460 typeof(T<<T) opShl(T)(T rhs) { return get!(T) << rhs; } /// ditto
461 typeof(T<<T) opShl_r(T)(T lhs) { return lhs << get!(T); } /// ditto
462 typeof(T>>T) opShr(T)(T rhs) { return get!(T) >> rhs; } /// ditto
463 typeof(T>>T) opShr_r(T)(T lhs) { return lhs >> get!(T); } /// ditto
464 typeof(T>>>T) opUShr(T)(T rhs) { return get!(T) >>> rhs; } /// ditto
465 typeof(T>>>T) opUShr_r(T)(T lhs){ return lhs >>> get!(T); } /// ditto
466 typeof(T~T) opCat(T)(T rhs) { return get!(T) ~ rhs; } /// ditto
467 typeof(T~T) opCat_r(T)(T lhs) { return lhs ~ get!(T); } /// ditto
468
469 Variant opAddAssign(T)(T value) { return (*this = get!(T) + value); } /// ditto
470 Variant opSubAssign(T)(T value) { return (*this = get!(T) - value); } /// ditto
471 Variant opMulAssign(T)(T value) { return (*this = get!(T) * value); } /// ditto
472 Variant opDivAssign(T)(T value) { return (*this = get!(T) / value); } /// ditto
473 Variant opModAssign(T)(T value) { return (*this = get!(T) % value); } /// ditto
474 Variant opAndAssign(T)(T value) { return (*this = get!(T) & value); } /// ditto
475 Variant opOrAssign(T)(T value) { return (*this = get!(T) | value); } /// ditto
476 Variant opXorAssign(T)(T value) { return (*this = get!(T) ^ value); } /// ditto
477 Variant opShlAssign(T)(T value) { return (*this = get!(T) << value); } /// ditto
478 Variant opShrAssign(T)(T value) { return (*this = get!(T) >> value); } /// ditto
479 Variant opUShrAssign(T)(T value){ return (*this = get!(T) >>> value); } /// ditto
480 Variant opCatAssign(T)(T value) { return (*this = get!(T) ~ value); } /// ditto
481
482 /**
483 * The following operators can be used with Variants on both sides. Note
484 * that these operators do not follow the standard rules of
485 * implicit conversions.
486 */
487 int opEquals(T)(T rhs)
488 {
489 static if( is( T == Variant ) )
490 return opEqualsVariant(rhs);
491 else
492 return get!(T) == rhs;
493 }
494
495 /// ditto
496 int opCmp(T)(T rhs)
497 {
498 static if( is( T == Variant ) )
499 return opCmpVariant(rhs);
500 else
501 {
502 auto lhs = get!(T);
503 return (lhs < rhs) ? -1 : (lhs == rhs) ? 0 : 1;
504 }
505 }
506
507 /// ditto
508 hash_t toHash()
509 {
510 return type.getHash(data.ptr);
511 }
512
513 /**
514 * Performs "stringification" of the value stored within the Variant. In
515 * the case of the Variant having no assigned value, it will return the
516 * string "Variant.init".
517 *
518 * Returns:
519 * The string representation of the value contained within the Variant.
520 */
521 char[] toString()
522 {
523 return type.toString;
524 }
525
526 /**
527 * This can be used to retrieve the TypeInfo for the currently stored
528 * value.
529 */
530 TypeInfo type()
531 {
532 return _type;
533 }
534
535 private:
536 TypeInfo _type = typeid(void);
537 AtomicTypes value;
538
539 TypeInfo type(TypeInfo v)
540 {
541 return (_type = v);
542 }
543
544 int opEqualsVariant(Variant rhs)
545 {
546 if( type != rhs.type ) return false;
547 return cast(bool) type.equals(data.ptr, rhs.data.ptr);
548 }
549
550 int opCmpVariant(Variant rhs)
551 {
552 if( type != rhs.type )
553 throw new VariantTypeMismatchException(type, rhs.type);
554 return type.compare(data.ptr, rhs.data.ptr);
555 }
556
557 void[] data()
558 {
559 if( type.tsize <= value.data.length )
560 return cast(void[])(value.data);
561 else
562 return value.arr;
563 }
564 }
565
566 debug( UnitTest )
567 {
568 unittest
569 {
570 Variant v;
571 assert( v.isA!(void), v.type.toString );
572 assert( v.isEmpty, v.type.toString );
573
574 v = 42;
575 assert( v.isA!(int), v.type.toString );
576 assert( v.isImplicitly!(long), v.type.toString );
577 assert( v.isImplicitly!(ulong), v.type.toString );
578 assert( !v.isImplicitly!(uint), v.type.toString );
579 assert( v.get!(int) == 42 );
580 assert( v.get!(long) == 42L );
581 assert( v.get!(ulong) == 42uL );
582
583 v.clear;
584 assert( v.isA!(void), v.type.toString );
585 assert( v.isEmpty, v.type.toString );
586
587 v = "Hello, World!"c;
588 assert( v.isA!(char[]), v.type.toString );
589 assert( !v.isImplicitly!(wchar[]), v.type.toString );
590 assert( v.get!(char[]) == "Hello, World!" );
591
592 v = [1,2,3,4,5];
593 assert( v.isA!(int[]), v.type.toString );
594 assert( v.get!(int[]) == [1,2,3,4,5] );
595
596 v = 3.1413;
597 assert( v.isA!(double), v.type.toString );
598 assert( v.isImplicitly!(real), v.type.toString );
599 assert( !v.isImplicitly!(float), v.type.toString );
600 assert( v.get!(double) == 3.1413 );
601
602 auto u = Variant(v);
603 assert( u.isA!(double), u.type.toString );
604 assert( u.get!(double) == 3.1413 );
605
606 v = 38;
607 assert( v + 4 == 42 );
608 assert( 4 + v == 42 );
609 assert( v - 4 == 34 );
610 assert( 4 - v == -34 );
611 assert( v * 2 == 76 );
612 assert( 2 * v == 76 );
613 assert( v / 2 == 19 );
614 assert( 2 / v == 0 );
615 assert( v % 2 == 0 );
616 assert( 2 % v == 2 );
617 assert( (v & 6) == 6 );
618 assert( (6 & v) == 6 );
619 assert( (v | 9) == 47 );
620 assert( (9 | v) == 47 );
621 assert( (v ^ 5) == 35 );
622 assert( (5 ^ v) == 35 );
623 assert( v << 1 == 76 );
624 assert( 1 << Variant(2) == 4 );
625 assert( v >> 1 == 19 );
626 assert( 4 >> Variant(2) == 1 );
627
628 assert( Variant("abc") ~ "def" == "abcdef" );
629 assert( "abc" ~ Variant("def") == "abcdef" );
630
631 v = 38; v += 4; assert( v == 42 );
632 v = 38; v -= 4; assert( v == 34 );
633 v = 38; v *= 2; assert( v == 76 );
634 v = 38; v /= 2; assert( v == 19 );
635 v = 38; v %= 2; assert( v == 0 );
636 v = 38; v &= 6; assert( v == 6 );
637 v = 38; v |= 9; assert( v == 47 );
638 v = 38; v ^= 5; assert( v == 35 );
639 v = 38; v <<= 1; assert( v == 76 );
640 v = 38; v >>= 1; assert( v == 19 );
641
642 v = "abc"; v ~= "def"; assert( v == "abcdef" );
643
644 assert( Variant(0) < Variant(42) );
645 assert( Variant(42) > Variant(0) );
646 assert( Variant(21) == Variant(21) );
647 assert( Variant(0) != Variant(42) );
648 assert( Variant("bar") == Variant("bar") );
649 assert( Variant("foo") != Variant("bar") );
650 {
651 auto v1 = Variant(42);
652 auto v2 = Variant("foo");
653 auto v3 = Variant(1+2.0i);
654
655 int[Variant] hash;
656 hash[v1] = 0;
657 hash[v2] = 1;
658 hash[v3] = 2;
659
660 assert( hash[v1] == 0 );
661 assert( hash[v2] == 1 );
662 assert( hash[v3] == 2 );
663 }
664 {
665 int[char[]] hash;
666 hash["a"] = 1;
667 hash["b"] = 2;
668 hash["c"] = 3;
669 Variant vhash = hash;
670
671 assert( vhash.get!(int[char[]])["a"] == 1 );
672 assert( vhash.get!(int[char[]])["b"] == 2 );
673 assert( vhash.get!(int[char[]])["c"] == 3 );
674 }
675 }
676 }
677