Mercurial > projects > ldc
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 |