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