Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

root/reflect/reflect/RuntimeTraits.d

Revision 251, 20.0 kB (checked in by dhasenan, 16 years ago)

More testing

Line 
1 /** Provides runtime traits, which provide much of the functionality of tango.core.Traits and
2  * is-expressions, as well as some functionality that is only available at runtime, using
3  * runtime type information.
4  *
5  * Authors: Chris Wright (dhasenan) <dhasenan@gmail.com>
6  * License: BSD-style; others available on request
7  * Copyright (c) 2009, CHRISTOPHER WRIGHT
8  * All rights reserved.
9  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
13  *      in the documentation and/or other materials provided with the distribution.
14  * The names of the contributors to this product may not be used to endorse or promote products derived from this software without specific prior
15  *      written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
22  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 module reflect.RuntimeTraits;
25
26 /// If the given type represents a typedef, return the actual type.
27 TypeInfo realType (TypeInfo type)
28 {
29     // TypeInfo_Typedef.next() doesn't return the actual type.
30     // I think it returns TypeInfo_Typedef.base.next().
31     // So, a slightly different method.
32     // This could be a typedef to a typedef.
33     // Recursion is not incredibly efficient.
34     TypeInfo_Typedef def;
35     while ((def = cast(TypeInfo_Typedef)type) !is null)
36     {
37         type = def.base;
38     }
39     return type;
40 }
41
42 /// If the given type represents a class, return its ClassInfo; else return null;
43 ClassInfo asClass (TypeInfo type)
44 {
45     if (isInterface (type))
46     {
47         auto klass = cast(TypeInfo_Interface) type;
48         return klass.info;
49     }
50     if (isClass (type))
51     {
52         auto klass = cast(TypeInfo_Class) type;
53         return klass.info;
54     }
55     return null;
56 }
57
58 /** Returns true iff the second type is an ancestor of the other, or if the types are the same.
59  * If either is null, returns false. */
60 bool isDerived (ClassInfo derived, ClassInfo base)
61 {
62     if (derived is null || base is null)
63         return false;
64     do
65         if (derived is base)
66             return true;
67     while ((derived = derived.base) !is null)
68     return false;
69 }
70
71 /** Returns true iff implementor implements the interface described
72  * by iface. This is an expensive operation (linear in the number of
73  * interfaces and base classes).
74  * This is not guaranteed to work if the implementor is an interface.
75  */
76 bool implements (ClassInfo implementor, ClassInfo iface)
77 {
78     foreach (info; applyInterfaces (implementor))
79     {
80         if (iface is info)
81             return true;
82     }
83     return false;
84 }
85
86 /** Returns true iff an instance of class test is implicitly castable to target.
87  * This is an expensive operation (isDerived + implements). */
88 bool isImplicitly (ClassInfo test, ClassInfo target)
89 {
90     // Keep isDerived first.
91     // isDerived will be much faster than implements.
92     return (isDerived (test, target) || implements (test, target));
93 }
94
95 /** Returns true iff an instance of type test is implicitly castable to target.
96  * If the types describe classes or interfaces, this is an expensive operation. */
97 // TODO TESTME
98 bool isImplicitly (TypeInfo test, TypeInfo target)
99 {
100     // A lot of special cases. This is ugly.
101     if (test is target)
102         return true;
103     if (isStaticArray (test) && isDynamicArray (target) && valueType (test) is valueType (target))
104     {
105         // you can implicitly cast static to dynamic (currently) if they
106         // have the same value type. Other casts should be forbidden.
107         return true;
108     }
109     auto klass1 = asClass (test);
110     auto klass2 = asClass (target);
111     if (klass1 && klass2)
112     {
113         return isImplicitly (klass1, klass2);
114     }
115     if (klass1 || klass2)
116     {
117         // no casts from class to non-class
118         return false;
119     }
120     if ((isSignedInteger (test) && isSignedInteger (target)) || (isUnsignedInteger (test) && isUnsignedInteger (target)) || (isFloat (
121             test) && isFloat (target)) || (isCharacter (test) && isCharacter (target)))
122     {
123         return test.tsize () <= target.tsize ();
124     }
125     if (isSignedInteger (test) && isUnsignedInteger (target))
126     {
127         // potential loss of data
128         return false;
129     }
130     if (isUnsignedInteger (test) && isSignedInteger (target))
131     {
132         // if the sizes are the same, you could be losing data
133         // the upper half of the range wraps around to negatives
134         // if the target type is larger, you can safely hold it
135         return test.tsize () < target.tsize ();
136     }
137     // delegates and functions: no can do
138     // pointers: no
139     // structs: no
140     return false;
141 }
142
143 ///
144 ClassInfo[] baseClasses (ClassInfo type)
145 {
146     if (type is null)
147         return null;
148     ClassInfo[] types;
149     while ((type = type.base) !is null)
150         types ~= type;
151     return types;
152 }
153
154 /** Returns a list of all interfaces that this type implements, directly
155  * or indirectly. This includes base interfaces of types the class implements,
156  * and interfaces that base classes implement, and base interfaces of interfaces
157  * that base classes implement. This is an expensive operation. */
158 ClassInfo[] baseInterfaces (ClassInfo type)
159 {
160     if (type is null)
161         return null;
162     ClassInfo[] types = directInterfaces (type);
163     while ((type = type.base) !is null)
164     {
165         types ~= interfaceGraph (type);
166     }
167     return types;
168 }
169
170 /** Returns all the interfaces that this type directly implements, including
171  * inherited interfaces. This is an expensive operation.
172  *
173  * Examples:
174  * ---
175  * interface I1 {}
176  * interface I2 : I1 {}
177  * class A : I2 {}
178  *
179  * auto interfaces = interfaceGraph (A.classinfo);
180  * // interfaces = [I1.classinfo, I2.classinfo]
181  * ---
182  *
183  * ---
184  * interface I1 {}
185  * interface I2 {}
186  * class A : I1 {}
187  * class B : A, I2 {}
188  *
189  * auto interfaces = interfaceGraph (B.classinfo);
190  * // interfaces = [I2.classinfo]
191  * ---
192  */
193 ClassInfo[] interfaceGraph (ClassInfo type)
194 {
195     ClassInfo[] info;
196     foreach (iface; type.interfaces)
197     {
198         info ~= iface.classinfo;
199         info ~= interfaceGraph (iface.classinfo);
200     }
201     return info;
202 }
203
204 /** Iterate through all interfaces that type implements, directly or indirectly, including base interfaces. */
205 struct applyInterfaces
206 {
207     ///
208     static applyInterfaces opCall (ClassInfo type)
209     {
210         applyInterfaces apply;
211         apply.type = type;
212         return apply;
213     }
214
215     ///
216     int opApply (int delegate (ref ClassInfo) dg)
217     {
218         int result = 0;
219         for (; type; type = type.base)
220         {
221             foreach (iface; type.interfaces)
222             {
223                 result = dg (iface.classinfo);
224                 if (result)
225                     return result;
226                 result = applyInterfaces (iface.classinfo).opApply (dg);
227                 if (result)
228                     return result;
229             }
230         }
231         return result;
232     }
233
234     ClassInfo type;
235 }
236
237 ///
238 ClassInfo[] baseTypes (ClassInfo type)
239 {
240     if (type is null)
241         return null;
242     return baseClasses (type) ~ baseInterfaces (type);
243 }
244
245 ///
246 // TODO TESTME
247 ModuleInfo moduleOf (ClassInfo type)
248 {
249     foreach (modula; ModuleInfo)
250         foreach (klass; modula.localClasses)
251             if (klass is type)
252                 return modula;
253     return null;
254 }
255
256 /// Returns a list of interfaces that this class directly implements.
257 ClassInfo[] directInterfaces (ClassInfo type)
258 {
259     ClassInfo[] types;
260     foreach (iface; type.interfaces)
261         types ~= iface.classinfo;
262     return types;
263 }
264
265 /** Returns a list of all types that are derived from the given type. This does not
266  * count interfaces; that is, if type is an interface, you will only get derived
267  * interfaces back. It is an expensive operations. */
268 // TODO TESTME
269 ClassInfo[] derivedTypes (ClassInfo type)
270 {
271     ClassInfo[] types;
272     foreach (modula; ModuleInfo)
273         foreach (klass; modula.localClasses)
274             if (isDerived (klass, type) && (klass !is type))
275                 types ~= klass;
276     return types;
277 }
278
279 ///
280 bool isDynamicArray (TypeInfo type)
281 {
282     // This implementation is evil.
283     // Array typeinfos are named TypeInfo_A?, and defined individually for each
284     // possible type aside from structs. For example, typeinfo for int[] is
285     // TypeInfo_Ai; for uint[], TypeInfo_Ak.
286     // So any TypeInfo with length 11 and starting with TypeInfo_A is an array
287     // type.
288     // Also, TypeInfo_Array is an array type.
289     type = realType (type);
290     return ((type.classinfo.name[9] == 'A') && (type.classinfo.name.length == 11)) || ((cast(TypeInfo_Array) type) !is null);
291 }
292
293 ///
294 bool isStaticArray (TypeInfo type)
295 {
296     type = realType (type);
297     return (cast(TypeInfo_StaticArray) type) !is null;
298 }
299
300 /** Returns true iff the given type is a dynamic or static array (false for associative
301  * arrays and non-arrays). */
302 bool isArray (TypeInfo type)
303 {
304     type = realType (type);
305     return isDynamicArray (type) || isStaticArray (type);
306 }
307
308 ///
309 bool isAssociativeArray (TypeInfo type)
310 {
311     type = realType (type);
312     return (cast(TypeInfo_AssociativeArray) type) !is null;
313 }
314
315 ///
316 bool isCharacter (TypeInfo type)
317 {
318     type = realType (type);
319     return (type is typeid(char) || type is typeid(wchar) || type is typeid(dchar));
320 }
321
322 ///
323 bool isString (TypeInfo type)
324 {
325     type = realType (type);
326     return isArray (type) && isCharacter (valueType (type));
327 }
328
329 ///
330 bool isUnsignedInteger (TypeInfo type)
331 {
332     type = realType (type);
333     return (type is typeid(uint) || type is typeid(ulong) || type is typeid(ushort) || type is typeid(ubyte));
334 }
335
336 ///
337 bool isSignedInteger (TypeInfo type)
338 {
339     type = realType (type);
340     return (type is typeid(int) || type is typeid(long) || type is typeid(short) || type is typeid(byte));
341 }
342
343 ///
344 bool isInteger (TypeInfo type)
345 {
346     type = realType (type);
347     return isSignedInteger (type) || isUnsignedInteger (type);
348 }
349
350 ///
351 bool isBool (TypeInfo type)
352 {
353     type = realType (type);
354     return (type is typeid(bool));
355 }
356
357 ///
358 bool isFloat (TypeInfo type)
359 {
360     type = realType (type);
361     return (type is typeid(float) || type is typeid(double) || type is typeid(real));
362 }
363
364 ///
365 bool isPrimitive (TypeInfo type)
366 {
367     type = realType (type);
368     return (isArray (type) || isAssociativeArray (type) || isCharacter (type) || isFloat (type) || isInteger (type));
369 }
370
371 /// Returns true iff the given type represents an interface.
372 bool isInterface (TypeInfo type)
373 {
374     type = realType (type);
375     return (cast(TypeInfo_Interface) type) !is null;
376 }
377
378 ///
379 bool isPointer (TypeInfo type)
380 {
381     type = realType (type);
382     return (cast(TypeInfo_Pointer) type) !is null;
383 }
384
385 /** Returns true iff the type represents a D object.
386  * Returns true for interfaces and classes, except if they implement
387  * IUnknown. This is an O(1) operation despite checking for IUnknown. */
388 bool isDObject (TypeInfo type)
389 {
390     auto klass = asClass(type);
391     return (klass && isDObject (klass));
392 }
393
394 /// ditto
395 bool isDObject (ClassInfo type)
396 {
397     return !isComObject (type);
398 }
399
400 /** Returns true iff the type represents a COM object -- if it implements
401  * IUnknown. This is an O(1) operation despite checking for IUnknown. */
402 bool isComObject (TypeInfo type)
403 {
404     auto klass = asClass(type);
405     return (klass && isComObject (klass));
406 }
407
408 /// ditto
409 bool isComObject (ClassInfo type)
410 {
411     const IunknownFlag = 1;
412     return (type.flags & IunknownFlag) != 0;
413 }
414
415 /** Returns true for classes or interfaces.
416  * If you think you might come across COM objects, use isDObject instead. */
417 bool isObject (TypeInfo type)
418 {
419     return asClass (type) !is null;
420 }
421
422 /// Returns true iff the type represents a class (false for interfaces).
423 bool isClass (TypeInfo type)
424 {
425     type = realType (type);
426     return (cast(TypeInfo_Class) type) !is null;
427 }
428
429 ///
430 bool isStruct (TypeInfo type)
431 {
432     type = realType (type);
433     return (cast(TypeInfo_Struct) type) !is null;
434 }
435
436 ///
437 bool isFunction (TypeInfo type)
438 {
439     type = realType (type);
440     return ((cast(TypeInfo_Function) type) !is null) || ((cast(TypeInfo_Delegate) type) !is null);
441 }
442
443 /** Returns true iff the given type is a reference type. */
444 // TODO TESTME
445 bool isReferenceType (TypeInfo type)
446 {
447     return isClass (type) || isPointer (type) || isDynamicArray (type) || isAssociativeArray (type);
448 }
449
450 /** Returns true iff the given type represents a user-defined type.
451  * This does not include functions, delegates, aliases, or typedefs. */
452 bool isUserDefined (TypeInfo type)
453 {
454     return isClass (type) || isStruct (type);
455 }
456
457 /** Returns true for all value types, false for all reference types.
458  * For functions and delegates, returns false (is this the way it should be?). */
459 bool isValueType (TypeInfo type)
460 {
461     // Is a blacklist really better than a whitelist?
462     return !(isDynamicArray (type) || isAssociativeArray (type) || isPointer (type) || isClass (type) || isFunction (type));
463 }
464
465 /** The key type of the given type. For an array, size_t; for an associative
466  * array T[U], U. */
467 TypeInfo keyType (TypeInfo type)
468 {
469     type = realType (type);
470     auto assocArray = cast(TypeInfo_AssociativeArray) type;
471     if (assocArray)
472         return assocArray.key;
473     if (isArray (type))
474         return typeid(size_t);
475     return null;
476 }
477
478 /** The value type of the given type -- given T[] or T[n], T; given T[U],
479  * T; given T*, T; anything else, null. */
480 TypeInfo valueType (TypeInfo type)
481 {
482     type = realType (type);
483     if (isArray (type))
484         return type.next;
485     auto assocArray = cast(TypeInfo_AssociativeArray) type;
486     if (assocArray)
487         return assocArray.value;
488     auto pointer = cast(TypeInfo_Pointer) type;
489     if (pointer)
490         return pointer.m_next;
491     return null;
492 }
493
494 /** If the given type represents a delegate or function, the return type
495  * of that function. Otherwise, null. */
496 TypeInfo returnType (TypeInfo type)
497 {
498     type = realType (type);
499     auto delegat = cast(TypeInfo_Delegate) type;
500     if (delegat)
501         return delegat.next;
502     auto func = cast(TypeInfo_Function) type;
503     if (func)
504         return func.next;
505     return null;
506 }
507
508 version (RuntimeTraitsTests)
509 {
510     import tango.io.Stdout;
511
512     interface I1
513     {
514     }
515
516     interface I2
517     {
518     }
519
520     interface I3
521     {
522     }
523
524     interface I4
525     {
526     }
527
528     interface ISub : I3
529     {
530     }
531
532     class A
533     {
534     }
535
536     class B : A, I1
537     {
538     }
539
540     class C : B, I2, I3
541     {
542     }
543
544     class D : A, I1
545     {
546         int foo (int i)
547         {
548             return i;
549         }
550     }
551
552     struct S1
553     {
554     }
555
556     unittest {
557         Stdout.formatln ("Struct unittest");
558         // Struct-related stuff.
559         auto type = typeid(S1);
560         assert (isStruct (type));
561         assert (isValueType (type));
562         assert (isUserDefined (type));
563         assert (!isClass (type));
564         assert (!isPointer (type));
565         assert (null is returnType (type));
566         assert (!isPrimitive (type));
567         assert (valueType (type) is null);
568     }
569
570     unittest {
571         Stdout.formatln ("Class unittest");
572         auto type = A.classinfo;
573         assert (isUserDefined (typeid(A)));
574         assert (baseTypes (type) == [Object.classinfo]);
575         assert (baseClasses (type) == [Object.classinfo]);
576         assert (baseInterfaces (type).length == 0);
577         type = C.classinfo;
578         assert (baseClasses (type) == [B.classinfo, A.classinfo, Object.classinfo]);
579         assert (baseInterfaces (type) == [I2.classinfo, I3.classinfo, I1.classinfo]);
580         assert (baseTypes (type) == [B.classinfo, A.classinfo, Object.classinfo, I2.classinfo, I3.classinfo,
581                 I1.classinfo]);
582     }
583
584     unittest {
585         Stdout.formatln ("Miscellaneous type unittest");
586         assert (isPointer (typeid(S1*)));
587         assert (isArray (typeid(S1[])));
588         assert (valueType (typeid(S1*)) is typeid(S1));
589         auto d = new D;
590         assert (returnType (typeid(typeof(&d.foo))) is typeid(int));
591         assert (isFloat (typeid(real)));
592         assert (isFloat (typeid(double)));
593         assert (isFloat (typeid(float)));
594         assert (!isFloat (typeid(creal)));
595         assert (!isFloat (typeid(cdouble)));
596         assert (!isInteger (typeid(float)));
597         assert (!isInteger (typeid(creal)));
598         assert (isInteger (typeid(ulong)));
599         assert (isInteger (typeid(ubyte)));
600         assert (isCharacter (typeid(char)));
601         assert (isCharacter (typeid(wchar)));
602         assert (isCharacter (typeid(dchar)));
603         assert (!isCharacter (typeid(ubyte)));
604         assert (isArray (typeid(typeof("hello"))));
605         assert (isCharacter (typeid(typeof("hello"[0]))));
606         assert (valueType (typeid(typeof("hello"))) is typeid(typeof('h')));
607         assert (isString (typeid(typeof("hello"))), typeof("hello").stringof);
608         auto staticString = typeid(typeof("hello"d));
609         auto dynamicString = typeid(typeof("hello"d[0 .. $]));
610         assert (isString (staticString));
611         assert (isStaticArray (staticString));
612         assert (isDynamicArray (dynamicString), dynamicString.toString () ~ dynamicString.classinfo.name);
613         assert (isString (dynamicString));
614
615         auto type = typeid(int[char[]]);
616         assert (valueType (type) is typeid(int), valueType (type).toString ());
617         assert (keyType (type) is typeid(char[]), keyType (type).toString ());
618         void delegate (int) dg = (int i)
619         {
620         };
621         assert (returnType (typeid(typeof(dg))) is typeid(void));
622         assert (returnType (typeid(int delegate (int))) is typeid(int));
623
624         assert (!isDynamicArray (typeid(int[4])));
625         assert (isStaticArray (typeid(int[4])));
626     }
627
628     unittest {
629         typedef int myint;
630         typedef myint mymyint;
631         assert (typeid(myint) !is null, "null typeid(myint)");
632         assert (isInteger (typeid(myint)));
633         assert (realType(typeid(mymyint)) is typeid(int));
634     }
635
636     unittest
637     {
638         TypeInfo info = typeid(I1);
639         assert (asClass(info) is I1.classinfo, "asClass: wrong for interface type");
640         info = typeid(A);
641         assert (asClass(info) is A.classinfo, "asClass: wrong for interface type");
642     }
643
644     unittest
645     {
646         assert (isDerived(B.classinfo, A.classinfo));
647         assert (isDerived(C.classinfo, B.classinfo));
648         assert (isDerived(C.classinfo, A.classinfo));
649         assert (!isDerived(A.classinfo, B.classinfo));
650         assert (!isDerived(I1.classinfo, B.classinfo));
651         assert (!isDerived(B.classinfo, I1.classinfo));
652         assert (!isDerived(B.classinfo, null));
653     }
654
655     unittest
656     {
657         assert (implements (C.classinfo, I1.classinfo));
658         assert (implements (C.classinfo, I2.classinfo));
659         assert (!implements (A.classinfo, I2.classinfo));
660         assert (!implements (D.classinfo, I2.classinfo));
661         // implements doesn't work right on interfaces
662         //assert (implements(ISub.classinfo, I4.classinfo));
663         //assert (!implements(I1.classinfo, I2.classinfo));
664         //assert (!implements(I4.classinfo, ISub.classinfo));
665     }
666
667     unittest
668     {
669         assert (isImplicitly(B.classinfo, A.classinfo));
670         assert (isImplicitly(C.classinfo, B.classinfo));
671         assert (isImplicitly(C.classinfo, A.classinfo));
672         assert (!isImplicitly(A.classinfo, B.classinfo));
673         assert (!isImplicitly(I1.classinfo, B.classinfo));
674         assert (isImplicitly(B.classinfo, I1.classinfo));
675         assert (isImplicitly (C.classinfo, I1.classinfo));
676         assert (isImplicitly (C.classinfo, I2.classinfo));
677         assert (!isImplicitly (A.classinfo, I2.classinfo));
678         assert (!isImplicitly (D.classinfo, I2.classinfo));
679     }
680
681     unittest
682     {
683         // isImplicitly for class/interface types
684         assert (isImplicitly(typeid(B), typeid(A)));
685         assert (isImplicitly(typeid(C), typeid(B)));
686         assert (isImplicitly(typeid(C), typeid(A)));
687         assert (!isImplicitly(typeid(A), typeid(B)));
688         assert (!isImplicitly(typeid(I1), typeid(B)));
689         assert (isImplicitly(typeid(B), typeid(I1)));
690         assert (isImplicitly (typeid(C), typeid(I1)));
691         assert (isImplicitly (typeid(C), typeid(I2)));
692         assert (!isImplicitly (typeid(A), typeid(I2)));
693         assert (!isImplicitly (typeid(D), typeid(I2)));
694     }
695
696     unittest
697     {
698         // isImplicitly for arrays
699         assert (isImplicitly(typeid(int[4]), typeid(int[])));
700         assert (isImplicitly(typeid(long[1][4]), typeid(long[1][])));
701         // different value types
702         assert (!isImplicitly(typeid(int[4]), typeid(char[])));
703         assert (!isImplicitly(typeid(int[]), typeid(char[])));
704         // dynamic to static
705         assert (!isImplicitly(typeid(int[]), typeid(int[4])));
706     }
707
708     // The magic of D -- it doesn't matter where IUnknown is declared
709     interface IUnknown {}
710     class ComObj : IUnknown
711     {
712     }
713
714     unittest
715     {
716         assert (isComObject(typeid(ComObj)));
717         assert (isComObject(ComObj.classinfo));
718         assert (!isDObject(typeid(ComObj)));
719         assert (!isDObject(ComObj.classinfo));
720         assert (!isComObject(typeid(A)));
721         assert (!isComObject(A.classinfo));
722         assert (isDObject(typeid(A)));
723         assert (isDObject(A.classinfo));
724         assert (isObject(typeid(ComObj)));
725         assert (isObject(typeid(A)));
726         assert (isObject(typeid(I1)));
727         assert (!isObject(typeid(S1)));
728         assert (!isObject(typeid(int)));
729     }
730
731     void main ()
732     {
733     }
734 }
Note: See TracBrowser for help on using the browser.