]> git.wincent.com - WOTest.git/blob - NSValue+WOTest.m
da1dfb41d0e78d0cc4ebc671db371726b335121f
[WOTest.git] / NSValue+WOTest.m
1 //
2 //  NSValue+WOTest.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 09 June 2005.
6 //
7 //  Copyright 2005-2007 Wincent Colaiuta.
8 //  This program is free software: you can redistribute it and/or modify
9 //  it under the terms of the GNU General Public License as published by
10 //  the Free Software Foundation, either version 3 of the License, or
11 //  (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 //
21
22 // category header
23 #import "NSValue+WOTest.h"
24
25 // system headers
26 #import <objc/objc-runtime.h>
27
28 // framework headers
29 #import "WOTest.h"
30 #import "NSObject+WOTest.h"
31 #import "NSScanner+WOTest.h"
32 #import "NSString+WOTest.h"
33
34 @implementation NSValue (WOTest)
35
36 #pragma mark -
37 #pragma mark Value creation methods
38
39 + (NSValue *)WOTest_valueWithChar:(char)aChar
40 {
41     return [NSValue value:&aChar withObjCType:@encode(char)];
42 }
43
44 + (NSValue *)WOTest_valueWithInt:(int)anInt
45 {
46     return [NSValue value:&anInt withObjCType:@encode(int)];
47 }
48
49 + (NSValue *)WOTest_valueWithShort:(short)aShort
50 {
51     return [NSValue value:&aShort withObjCType:@encode(short)];
52 }
53
54 + (NSValue *)WOTest_valueWithLong:(long)aLong
55 {
56     return [NSValue value:&aLong withObjCType:@encode(long)];
57 }
58
59 + (NSValue *)WOTest_valueWithLongLong:(long long)aLongLong
60 {
61     return [NSValue value:&aLongLong withObjCType:@encode(long long)];
62 }
63
64 + (NSValue *)WOTest_valueWithUnsignedChar:(unsigned char)anUnsignedChar
65 {
66     return [NSValue value:&anUnsignedChar withObjCType:@encode(unsigned char)];
67 }
68
69 + (NSValue *)WOTest_valueWithUnsignedInt:(unsigned int)anUnsignedInt
70 {
71     return [NSValue value:&anUnsignedInt withObjCType:@encode(unsigned int)];
72 }
73
74 + (NSValue *)WOTest_valueWithUnsignedShort:(unsigned short)anUnsignedShort
75 {
76     return [NSValue value:&anUnsignedShort withObjCType:@encode(unsigned short)];
77 }
78
79 + (NSValue *)WOTest_valueWithUnsignedLong:(unsigned long)anUnsignedLong
80 {
81     return [NSValue value:&anUnsignedLong withObjCType:@encode(unsigned long)];
82 }
83
84 + (NSValue *)WOTest_valueWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong
85 {
86     return [NSValue value:&anUnsignedLongLong withObjCType:@encode(unsigned long long)];
87 }
88
89 + (NSValue *)WOTest_valueWithFloat:(float)aFloat
90 {
91     return [NSValue value:&aFloat withObjCType:@encode(float)];
92 }
93
94 + (NSValue *)WOTest_valueWithDouble:(double)aDouble
95 {
96     return [NSValue value:&aDouble withObjCType:@encode(double)];
97 }
98
99 + (NSValue *)WOTest_valueWithC99Bool:(_Bool)aC99Bool
100 {
101     return [NSValue value:&aC99Bool withObjCType:@encode(_Bool)];
102 }
103
104 + (NSValue *)WOTest_valueWithConstantCharacterString:(const char *)aConstantCharString
105 {
106     return [NSValue value:&aConstantCharString withObjCType:@encode(const char *)];
107 }
108
109 + (NSValue *)WOTest_valueWithCharacterString:(char *)aCharacterString
110 {
111     return [NSValue value:&aCharacterString withObjCType:@encode(char *)];
112 }
113
114 + (NSValue *)WOTest_valueWithObject:(id)anObject
115 {
116     return [NSValue value:&anObject withObjCType:@encode(id)];
117 }
118
119 + (NSValue *)WOTest_valueWithClass:(Class)aClass
120 {
121     return [NSValue value:&aClass withObjCType:@encode(Class)];
122 }
123
124 + (NSValue *)WOTest_valueWithSelector:(SEL)aSelector
125 {
126     return [NSValue value:&aSelector withObjCType:@encode(SEL)];
127 }
128
129 #pragma mark -
130 #pragma mark Parsing type strings
131
132 // TODO: investigate whether NSGetSizeAndAlignment() could save some code here...
133 + (size_t)WOTest_maximumEmbeddedSizeForType:(NSString *)typeString
134 {
135     NSParameterAssert(typeString != nil);
136     size_t size = 16; // worst case scenario
137
138     if ([self WOTest_typeIsCompound:typeString])
139         return [self WOTest_sizeForType:typeString];
140
141     if ([self WOTest_typeIsBitfield:typeString])
142         return sizeof(int);
143
144 #if defined (__i386__)
145
146     if ([self WOTest_typeIsC99Bool:typeString]                         ||
147         [self WOTest_typeIsUnsignedChar:typeString]                    ||
148         [self WOTest_typeIsChar:typeString]                            ||
149         [self WOTest_typeIsUnsignedChar:typeString])
150         size = 1;   // scalars of size/alignment 1
151     else if ([self WOTest_typeIsUnsignedShort:typeString]              ||
152              [self WOTest_typeIsShort:typeString])
153         size = 2;   // scalars of size/alignment 2
154     else if ([self WOTest_typeIsUnsignedInt:typeString]                ||
155              [self WOTest_typeIsInt:typeString]                        ||
156              [self WOTest_typeIsUnsignedLong:typeString]               ||
157              [self WOTest_typeIsLong:typeString]                       ||
158              [self WOTest_typeIsFloat:typeString])
159         size = 4;   // scalars of size/alignment 4
160     else if ([self WOTest_typeIsUnsignedLongLong:typeString]           ||
161              [self WOTest_typeIsLongLong:typeString]                   ||
162              [self WOTest_typeIsDouble:typeString])
163         size = 8;   // scalars of size/alignment 8
164     else if ([self WOTest_typeIsPointer:typeString]                    ||
165              [self WOTest_typeIsPointerToVoid:typeString]              ||
166              [self WOTest_typeIsObject:typeString]                     ||
167              [self WOTest_typeIsClass:typeString]                      ||
168              [self WOTest_typeIsSelector:typeString]                   ||
169              [self WOTest_typeIsCharacterString:typeString]            ||
170              [self WOTest_typeIsConstantCharacterString:typeString])
171         size = 4;   // pointers (size/alignment 4)
172     else
173         // documented in "Mac OS X ABI Function Call Guide" but not supported:
174         // long double        16 bytes
175         // vector (64 bits)   8 bytes
176         // vector (128 bits)  16 bytes
177         [NSException raise:NSInternalInconsistencyException
178                     format:@"Type %@ not supported by WOTest_maximumEmbeddedSizeForType:", typeString];
179
180 #elif defined (__ppc__)
181
182     if ([self WOTest_typeIsUnsignedChar:typeString]                    ||
183         [self WOTest_typeIsChar:typeString])
184         size = 1;   // scalars of size/alignment 1
185     else if ([self WOTest_typeIsUnsignedShort:typeString]              ||
186              [self WOTest_typeIsShort:typeString])
187         size = 2;   // scalars of size/alignment 2
188     else if ([self WOTest_typeIsC99Bool:typeString]                    ||
189              [self WOTest_typeIsUnsignedInt:typeString]                ||
190              [self WOTest_typeIsInt:typeString]                        ||
191              [self WOTest_typeIsUnsignedLong:typeString]               ||
192              [self WOTest_typeIsLong:typeString]                       ||
193              [self WOTest_typeIsFloat:typeString])
194         size = 4;   // scalars of size/alignment 4
195     else if ([self WOTest_typeIsUnsignedLongLong:typeString]           ||
196              [self WOTest_typeIsLongLong:typeString]                   ||
197              [self WOTest_typeIsDouble:typeString])
198         size = 8;   // scalars of size/alignment 8
199     else if ([self WOTest_typeIsPointer:typeString]                    ||
200              [self WOTest_typeIsPointerToVoid:typeString]              ||
201              [self WOTest_typeIsObject:typeString]                     ||
202              [self WOTest_typeIsClass:typeString]                      ||
203              [self WOTest_typeIsSelector:typeString]                   ||
204              [self WOTest_typeIsCharacterString:typeString]            ||
205              [self WOTest_typeIsConstantCharacterString:typeString])
206         size = 4;   // pointers (size/alignment 4)
207     else
208         // documented in "Mac OS X ABI Function Call Guide" but not supported:
209         // long double  8 bytes (Mac OS X < 10.4, GCC < 4.0)
210         // long double  16 bytes (Mac OS X >= 10.4, GCC >= 4.0)
211         // vector       16 bytes
212         [NSException raise:NSInternalInconsistencyException
213                     format:@"Type %@ not supported by WOTest_maximumEmbeddedSizeForType:", typeString];
214
215 #elif defined (__ppc64__)
216
217     if ([self WOTest_typeIsC99Bool:typeString]                         ||
218         [self WOTest_typeIsUnsignedChar:typeString]                    ||
219         [self WOTest_typeIsChar:typeString])
220         size = 1;   // scalars of size/alignment 1
221     else if ([self WOTest_typeIsUnsignedShort:typeString]              ||
222              [self WOTest_typeIsShort:typeString])
223         size = 2;   // scalars of size/alignment 2
224     else if ([self WOTest_typeIsUnsignedInt:typeString]                ||
225              [self WOTest_typeIsInt:typeString]                        ||
226              [self WOTest_typeIsFloat:typeString])
227         size = 4;   // scalars of size/alignment 4
228     else if ([self WOTest_typeIsUnsignedLong:typeString]               ||
229              [self WOTest_typeIsLong:typeString]                       ||
230              [self WOTest_typeIsUnsignedLongLong:typeString]           ||
231              [self WOTest_typeIsLongLong:typeString]                   ||
232              [self WOTest_typeIsDouble:typeString])
233         size = 8;   // scalars of size/alignment 8
234     else if ([self WOTest_typeIsPointer:typeString]                    ||
235              [self WOTest_typeIsPointerToVoid:typeString]              ||
236              [self WOTest_typeIsObject:typeString]                     ||
237              [self WOTest_typeIsClass:typeString]                      ||
238              [self WOTest_typeIsSelector:typeString]                   ||
239              [self WOTest_typeIsCharacterString:typeString]            ||
240              [self WOTest_typeIsConstantCharacterString:typeString])
241         size = 8;   // pointers (size/alignment 8)
242     else
243         // documented in "Mac OS X ABI Function Call Guide" but not supported:
244         // long double  16 bytes
245         // vector       16 bytes
246         [NSException raise:NSInternalInconsistencyException
247                     format:@"Type %@ not supported by WOTest_maximumEmbeddedSizeForType:", typeString];
248
249 #else
250
251 #error Unsupported architecture
252
253 #endif
254
255     return size;
256 }
257
258 + (size_t)WOTest_sizeForType:(NSString *)typeString
259 {
260     NSParameterAssert(typeString != nil);
261     size_t size = 0;
262
263     if ([self WOTest_typeIsChar:typeString])
264         size = sizeof(char);
265     else if ([self WOTest_typeIsInt:typeString])
266         size = sizeof(int);
267     else if ([self WOTest_typeIsShort:typeString])
268         size = sizeof(short);
269     else if ([self WOTest_typeIsLong:typeString])
270         size = sizeof(long);
271     else if ([self WOTest_typeIsLongLong:typeString])
272         size = sizeof(long long);
273     else if ([self WOTest_typeIsUnsignedChar:typeString])
274         size = sizeof(unsigned char);
275     else if ([self WOTest_typeIsUnsignedInt:typeString])
276         size = sizeof(unsigned int);
277     else if ([self WOTest_typeIsUnsignedShort:typeString])
278         size = sizeof(unsigned short);
279     else if ([self WOTest_typeIsUnsignedLong:typeString])
280         size = sizeof(unsigned long);
281     else if ([self WOTest_typeIsUnsignedLongLong:typeString])
282         size = sizeof(unsigned long long);
283     else if ([self WOTest_typeIsFloat:typeString])
284         size = sizeof(float);
285     else if ([self WOTest_typeIsDouble:typeString])
286         size = sizeof(double);
287     else if ([self WOTest_typeIsC99Bool:typeString])
288         size = sizeof(_Bool);
289     else if ([self WOTest_typeIsVoid:typeString])
290         size = sizeof(void);
291     else if ([self WOTest_typeIsConstantCharacterString:typeString])
292         size = sizeof(const char *);
293     else if ([self WOTest_typeIsCharacterString:typeString])
294         size = sizeof(char *);
295     else if ([self WOTest_typeIsObject:typeString])
296         size = sizeof(id);
297     else if ([self WOTest_typeIsClass:typeString])
298         size = sizeof(Class);
299     else if ([self WOTest_typeIsSelector:typeString])
300         size = sizeof(SEL);
301     else if ([self WOTest_typeIsPointerToVoid:typeString])
302         size = sizeof(void *);
303     else // handle complex types and special cases
304     {
305         // if is pointer
306         if ([self WOTest_typeIsPointer:typeString])
307             size = sizeof(void *);
308         else if ([self WOTest_typeIsArray:typeString])
309         {
310             NSScanner *scanner = [NSScanner scannerWithString:typeString];
311             unichar startMarker, endMarker;
312             int count;
313             NSString *elementType;
314             if ([scanner WOTest_scanCharacter:&startMarker] &&
315                 (startMarker == _C_ARY_B) && [scanner scanInt:&count] &&
316                 [scanner WOTest_scanTypeIntoString:&elementType] &&
317                 [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
318                 [scanner isAtEnd])
319             {
320                 // recursion
321                 size = [self WOTest_sizeForType:elementType] * count;
322             }
323             else
324                 [NSException raise:NSInternalInconsistencyException
325                             format:@"scanner error in sizeForType for type %@", typeString];
326         }
327         else if ([self WOTest_typeIsStruct:typeString])
328         {
329             NSScanner *scanner = [NSScanner scannerWithString:typeString];
330             unichar startMarker, endMarker;
331
332             if ([scanner WOTest_scanCharacter:&startMarker] &&
333                 (startMarker == _C_STRUCT_B))
334             {
335                 // scan optional identifier
336                 if ([scanner WOTest_scanIdentifierIntoString:nil])
337                     [scanner WOTest_scanCharacter:NULL]; // scan past "="
338
339                 NSString    *memberType;
340                 size_t      largestMember   = 0;
341                 while ([scanner WOTest_scanTypeIntoString:&memberType])
342                 {
343                     size_t memberSize = [self WOTest_maximumEmbeddedSizeForType:memberType];
344                     largestMember = MAX(largestMember, memberSize);
345
346                     if (memberSize != 0) // watch out for division by zero
347                     {
348                         // check for alignment gap
349                         size_t modulo = (size % memberSize);
350                         if (modulo != 0) // fill alignment gap
351                             size += (memberSize - modulo);
352                     }
353
354                     size += memberSize;
355                 }
356
357 #if defined (__i386__) || defined (__ppc64)
358
359                 // Special rules for i386:
360                 // 1. Composite data types (structs/arrays/unions) take on the alignment of the member with the highest alignment
361                 // 2. Size of composite type is a multiple of its alignment
362
363                 // Special rules for ppc64 (equivalent):
364                 // 1. Embedding alignment of composite types (array/struct) is same as largest embedding align of members.
365                 // 2. Total size of the composite is rounded up to multiple of its embedding alignment.
366
367                 // Special rules for ppc: None.
368
369                 if (largestMember != 0) // watch out for division by zero
370                 {
371                     // check for alignment gap
372                     size_t modulo = (size % largestMember);
373                     if (modulo != 0) // fill alignment gap
374                         size += (largestMember - modulo);
375                 }
376
377 #endif
378
379                 if ([scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_STRUCT_E) && [scanner isAtEnd])
380                     return size; // all done
381             }
382
383             [NSException raise:NSInternalInconsistencyException format:@"scanner error in sizeForType for type %@", typeString];
384         }
385         else if ([self WOTest_typeIsUnion:typeString])
386         {
387             NSScanner *scanner = [NSScanner scannerWithString:typeString];
388             unichar startMarker, endMarker;
389
390             if ([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_UNION_B))
391             {
392                 // scan optional identifier
393                 if ([scanner WOTest_scanIdentifierIntoString:nil])
394                     [scanner WOTest_scanCharacter:NULL]; // scan past "="
395
396                 NSString *memberType;
397                 while ([scanner WOTest_scanTypeIntoString:&memberType])
398                     // size of union is size of largest type in the union
399                     size = MAX(size, [self WOTest_maximumEmbeddedSizeForType:memberType]);
400
401                 if ([scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_UNION_E) && [scanner isAtEnd])
402                     return size; // all done
403             }
404
405             [NSException raise:NSInternalInconsistencyException format:@"scanner error in sizeForType for type %@", typeString];
406         }
407         else if ([self WOTest_typeIsBitfield:typeString])
408             size = sizeof(int);
409         else if ([self WOTest_typeIsUnknown:typeString])
410         {
411             // could be a function pointer, but could be something else
412             [NSException raise:NSInternalInconsistencyException format: @"Cannot calculate buffer size for type %@", typeString];
413         }
414         else // we officially have no idea whatsoever
415             [NSException raise:NSInternalInconsistencyException
416                         format:@"Cannot calculate buffer size for unknown type %@", typeString];
417     }
418
419     return size;
420 }
421
422 /*! Returns YES if \p typeString contains a numeric scalar value (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double, C99 _Bool). Returns NO if the receiver contains any other type, object or pointer (id, Class, SEL, void, char *, as well as arrays, structures and pointers). */
423 + (BOOL)WOTest_typeIsNumericScalar:(NSString *)typeString
424 {
425     if (!typeString) return NO;
426     return ([self WOTest_typeIsChar:typeString]                ||
427             [self WOTest_typeIsInt:typeString]                 ||
428             [self WOTest_typeIsShort:typeString]               ||
429             [self WOTest_typeIsLong:typeString]                ||
430             [self WOTest_typeIsLongLong:typeString]            ||
431             [self WOTest_typeIsUnsignedChar:typeString]        ||
432             [self WOTest_typeIsUnsignedInt:typeString]         ||
433             [self WOTest_typeIsUnsignedShort:typeString]       ||
434             [self WOTest_typeIsUnsignedLong:typeString]        ||
435             [self WOTest_typeIsUnsignedLongLong:typeString]    ||
436             [self WOTest_typeIsFloat:typeString]               ||
437             [self WOTest_typeIsDouble:typeString]              ||
438             [self WOTest_typeIsC99Bool:typeString]);
439 }
440
441 + (BOOL)WOTest_typeIsCompound:(NSString *)typeString
442 {
443     if (!typeString) return NO;
444     return ([self WOTest_typeIsStruct:typeString] || [self WOTest_typeIsUnion:typeString] || [self WOTest_typeIsArray:typeString]);
445 }
446
447 + (BOOL)WOTest_typeIsChar:(NSString *)typeString
448 {
449     if (!typeString) return NO;
450     const char *type = [typeString UTF8String];
451     return ((strlen(type) == 1) && *type == _C_CHR);
452 }
453
454 + (BOOL)WOTest_typeIsInt:(NSString *)typeString
455 {
456     if (!typeString) return NO;
457     const char *type = [typeString UTF8String];
458     return ((strlen(type) == 1) && *type == _C_INT);
459 }
460
461 + (BOOL)WOTest_typeIsShort:(NSString *)typeString
462 {
463     if (!typeString) return NO;
464     const char *type = [typeString UTF8String];
465     return ((strlen(type) == 1) && *type == _C_SHT);
466 }
467
468 + (BOOL)WOTest_typeIsLong:(NSString *)typeString
469 {
470     if (!typeString) return NO;
471     const char *type = [typeString UTF8String];
472     return ((strlen(type) == 1) && *type == _C_LNG);
473 }
474
475 + (BOOL)WOTest_typeIsLongLong:(NSString *)typeString
476 {
477     if (!typeString) return NO;
478     const char *type = [typeString UTF8String];
479     return ((strlen(type) == 1) && *type == _C_LNGLNG);
480 }
481
482 + (BOOL)WOTest_typeIsUnsignedChar:(NSString *)typeString
483 {
484     if (!typeString) return NO;
485     const char *type = [typeString UTF8String];
486     return ((strlen(type) == 1) && *type == _C_UCHR);
487 }
488
489 + (BOOL)WOTest_typeIsUnsignedInt:(NSString *)typeString
490 {
491     if (!typeString) return NO;
492     const char *type = [typeString UTF8String];
493     return ((strlen(type) == 1) && *type == _C_UINT);
494 }
495
496 + (BOOL)WOTest_typeIsUnsignedShort:(NSString *)typeString
497 {
498     if (!typeString) return NO;
499     const char *type = [typeString UTF8String];
500     return ((strlen(type) == 1) && *type == _C_USHT);
501 }
502
503 + (BOOL)WOTest_typeIsUnsignedLong:(NSString *)typeString
504 {
505     if (!typeString) return NO;
506     const char *type = [typeString UTF8String];
507     return ((strlen(type) == 1) && *type == _C_ULNG);
508 }
509
510 + (BOOL)WOTest_typeIsUnsignedLongLong:(NSString *)typeString
511 {
512     if (!typeString) return NO;
513     const char *type = [typeString UTF8String];
514     return ((strlen(type) == 1) && *type == _C_ULNGLNG);
515 }
516
517 + (BOOL)WOTest_typeIsFloat:(NSString *)typeString
518 {
519     if (!typeString) return NO;
520     const char *type = [typeString UTF8String];
521     return ((strlen(type) == 1) && *type == _C_FLT);
522 }
523
524 + (BOOL)WOTest_typeIsDouble:(NSString *)typeString
525 {
526     if (!typeString) return NO;
527     const char *type = [typeString UTF8String];
528     return ((strlen(type) == 1) && *type == _C_DBL);
529 }
530
531 + (BOOL)WOTest_typeIsC99Bool:(NSString *)typeString
532 {
533     if (!typeString) return NO;
534     const char *type = [typeString UTF8String];
535     return ((strlen(type) == 1) && *type == _C_99BOOL);
536 }
537
538 + (BOOL)WOTest_typeIsVoid:(NSString *)typeString
539 {
540     if (!typeString) return NO;
541     const char *type = [typeString UTF8String];
542     return ((strlen(type) == 1) && *type == _C_VOID);
543 }
544
545 + (BOOL)WOTest_typeIsConstantCharacterString:(NSString *)typeString
546 {
547     if (!typeString) return NO;
548     const char *type = [typeString UTF8String];
549     return ((strlen(type) == 2) && (strcmp(type, "r*") == 0));
550 }
551
552 + (BOOL)WOTest_typeIsCharacterString:(NSString *)typeString
553 {
554     if (!typeString) return NO;
555     const char *type = [typeString UTF8String];
556     return ((strlen(type) == 1) && *type == _C_CHARPTR);
557 }
558
559 + (BOOL)WOTest_typeIsObject:(NSString *)typeString
560 {
561     if (!typeString) return NO;
562     const char *type = [typeString UTF8String];
563     return ((strlen(type) == 1) && *type == _C_ID);
564 }
565
566 + (BOOL)WOTest_typeIsClass:(NSString *)typeString
567 {
568     if (!typeString) return NO;
569     const char *type = [typeString UTF8String];
570     return ((strlen(type) == 1) && *type == _C_CLASS);
571 }
572
573 + (BOOL)WOTest_typeIsSelector:(NSString *)typeString
574 {
575     if (!typeString) return NO;
576     const char *type = [typeString UTF8String];
577     return ((strlen(type) == 1) && *type == _C_SEL);
578 }
579
580 + (BOOL)WOTest_typeIsPointerToVoid:(NSString *)typeString
581 {
582     if (!typeString) return NO;
583     const char *type = [typeString UTF8String];
584     return ((strlen(type) == 2) && (strcmp(type, "^v") == 0));
585 }
586
587 + (BOOL)WOTest_typeIsPointer:(NSString *)typeString
588 {
589     NSScanner *scanner = [NSScanner scannerWithString:typeString];
590     return [scanner scanPointerIntoString:nil];
591 }
592
593 + (BOOL)WOTest_typeIsArray:(NSString *)typeString
594 {
595     NSScanner *scanner = [NSScanner scannerWithString:typeString];
596     return [scanner WOTest_scanArrayIntoString:nil];
597 }
598
599 + (BOOL)WOTest_typeIsStruct:(NSString *)typeString
600 {
601     NSScanner *scanner = [NSScanner scannerWithString:typeString];
602     return [scanner WOTest_scanStructIntoString:nil];
603 }
604
605 + (BOOL)WOTest_typeIsUnion:(NSString *)typeString
606 {
607     NSScanner *scanner = [NSScanner scannerWithString:typeString];
608     return [scanner WOTest_scanUnionIntoString:nil];
609 }
610
611 + (BOOL)WOTest_typeIsBitfield:(NSString *)typeString
612 {
613     NSScanner *scanner = [NSScanner scannerWithString:typeString];
614     return [scanner WOTest_scanBitfieldIntoString:nil];
615 }
616
617 + (BOOL)WOTest_typeIsUnknown:(NSString *)typeString
618 {
619     if (!typeString) return NO;
620     const char *type = [typeString UTF8String];
621     return ((strlen(type) == 1) && *type == _C_UNDEF);
622 }
623
624 #pragma mark -
625 #pragma mark High-level test methods
626
627 - (BOOL)WOTest_testIsEqualToValue:(NSValue *)otherValue
628 {
629     if (!otherValue) return NO;
630
631     if ([self WOTest_isObject])
632     {
633         if ([otherValue WOTest_isObject])
634         {
635             // can only compare objects with objects
636             id selfObject   = [self nonretainedObjectValue];
637             id otherObject  = [otherValue nonretainedObjectValue];
638
639             // avoid the message send if pointers are equal
640             // this also allows two nil object pointers to be considered equal, as they should
641             if (selfObject == otherObject)
642                 return YES;
643
644             @try {
645                 if (selfObject && otherObject && [NSObject WOTest_object:selfObject respondsToSelector:@selector(isEqual:)])
646                     return [selfObject isEqual:otherObject];
647             }
648             @catch (id e) {
649                 // fall through
650             }
651         }
652         else if ([otherValue WOTest_isNumericScalar])
653         {
654             // check for special case: comparing an object with nil
655             if (strcmp([otherValue objCType], @encode(typeof(nil))) == 0)
656             {
657                 typeof(nil) nilId = nil;
658                 NSValue *nilValue = [NSValue valueWithBytes:&nilId objCType:@encode(typeof(nil))];
659                 if ([otherValue WOTest_compare:nilValue] == NSOrderedSame) // comparing other value (nil) with self object
660                     return ([self nonretainedObjectValue] == nil);
661             }
662
663             // will raise exception (comparing numeric scalar with object)
664             return ([self WOTest_compare:otherValue] == NSOrderedSame);
665         }
666         else if ([otherValue WOTest_isPointerToVoid])
667             // encodings changed on Leopard such that comparisons such as WO_TEST_EQ(foo, nil) no longer worked;
668             // in this case foo is encoded as type "@" (object) and nil as "^v" (pointer to void)
669             // so we end up here and just compare pointers numerically
670             return ((void *)[self nonretainedObjectValue] == [otherValue pointerValue]);
671     }
672     else if ([self WOTest_isNumericScalar])
673     {
674         // check for special case: comparing with nil
675         typeof(nil) nilId = nil;
676         NSValue *nilValue = [NSValue valueWithBytes:&nilId objCType:@encode(typeof(nil))];
677         if ((strcmp([self objCType], @encode(typeof(nil))) == 0) && ([self WOTest_compare:nilValue] == NSOrderedSame))
678         {
679             // self is nil (or at least looks like nil)
680             if ([otherValue WOTest_isObject])                          // comparing self (nil) with otherObject
681                 return ([otherValue nonretainedObjectValue] == nil);
682             else if ([otherValue WOTest_isPointerToVoid])              // special case can compare to pointer to void if zero
683                 return ((id)[otherValue pointerValue] == nil);
684         }
685
686         // could raise exception (comparing numeric scalar with object)
687         return ([self WOTest_compare:otherValue] == NSOrderedSame);
688     }
689     else if ([self WOTest_isPointerToVoid])
690     {
691         if ([otherValue WOTest_isObject])
692             // special case for pointer-to-void vs object comparisons (necessary on Leopard)
693             return ([self pointerValue] == (void *)[otherValue nonretainedObjectValue]);
694         else if ([otherValue WOTest_isPointerToVoid])
695             // this special case already necessary on Leopard, otherwise nil-to-nil comparison fails
696             return ([self pointerValue] == [otherValue pointerValue]);
697
698         // fall through to standard case
699         return ([self WOTest_compare:otherValue] == NSOrderedSame);
700     }
701     else if (([self WOTest_isCharArray] || [self WOTest_isCharacterString] || [self WOTest_isConstantCharacterString]) &&
702              ([otherValue WOTest_isCharArray] || [otherValue WOTest_isCharacterString] ||
703               [otherValue WOTest_isConstantCharacterString]))
704     {
705         // another special case
706         NSString *selfString    = [self WOTest_stringValue];
707         NSString *otherString   = [otherValue WOTest_stringValue];
708         if (selfString && otherString)
709             return [selfString isEqualToString:otherString];
710     }
711
712     // fallback case (Class objects, SEL, structs, unions, pointers etc)
713     return [self isEqualToValue:otherValue];
714 }
715
716 - (BOOL)WOTest_testIsGreaterThanValue:(NSValue *)aValue
717 {
718     return ([self WOTest_compare:aValue] == NSOrderedDescending);
719 }
720
721 - (BOOL)WOTest_testIsLessThanValue:(NSValue *)aValue
722 {
723     return ([self WOTest_compare:aValue] == NSOrderedAscending);
724 }
725
726 - (BOOL)WOTest_testIsNotGreaterThanValue:(NSValue *)aValue
727 {
728     return ([self WOTest_compare:aValue] != NSOrderedDescending);
729 }
730
731 - (BOOL)WOTest_testIsNotLessThanValue:(NSValue *)aValue
732 {
733     return ([self WOTest_compare:aValue] != NSOrderedAscending);
734 }
735
736 - (NSComparisonResult)WOTest_compare:(NSValue *)aValue
737 {
738     if (!aValue)
739         [NSException raise:NSInvalidArgumentException format:@"cannot compare to nil"];
740
741     // object case
742     if ([self WOTest_isObject])
743     {
744         if (![aValue WOTest_isObject])
745             [NSException raise:NSInvalidArgumentException format:@"cannot compare object with non-object"];
746
747         id selfObject   = [self nonretainedObjectValue];
748         id otherObject  = [aValue nonretainedObjectValue];
749
750         if ([selfObject isKindOfClass:[otherObject class]] && [selfObject respondsToSelector:@selector(compare:)])
751             return ((NSComparisonResult (*)(id, SEL, id))objc_msgSend)(selfObject, @selector(compare:), otherObject);
752         else if ([otherObject isKindOfClass:[selfObject class]] && [otherObject respondsToSelector:@selector(compare:)])
753             return ((NSComparisonResult (*)(id, SEL, id))objc_msgSend)(otherObject, @selector(compare:), selfObject);
754
755         [NSException raise:NSInvalidArgumentException format:@"compared objects must be of same class and implement compare:"];
756     }
757
758     // numeric scalar case
759     if ([self WOTest_isNumericScalar] && [aValue WOTest_isNumericScalar])
760     {
761         if ([aValue WOTest_isChar])
762             return [self WOTest_compareWithChar:[aValue WOTest_charValue]];
763         else if ([aValue WOTest_isInt])
764             return [self WOTest_compareWithInt:[aValue WOTest_intValue]];
765         else if ([aValue WOTest_isShort])
766             return [self WOTest_compareWithShort:[aValue WOTest_shortValue]];
767         else if ([aValue WOTest_isLong])
768             return [self WOTest_compareWithLong:[aValue WOTest_longValue]];
769         else if ([aValue WOTest_isLongLong])
770             return [self WOTest_compareWithLongLong:[aValue WOTest_longLongValue]];
771         else if ([aValue WOTest_isUnsignedChar])
772             return [self WOTest_compareWithUnsignedChar:[aValue WOTest_unsignedCharValue]];
773         else if ([aValue WOTest_isUnsignedInt])
774             return [self WOTest_compareWithUnsignedInt:[aValue WOTest_unsignedIntValue]];
775         else if ([aValue WOTest_isUnsignedShort])
776             return [self WOTest_compareWithUnsignedShort:[aValue WOTest_unsignedShortValue]];
777         else if ([aValue WOTest_isUnsignedLong])
778             return [self WOTest_compareWithUnsignedLong:[aValue WOTest_unsignedLongValue]];
779         else if ([aValue WOTest_isUnsignedLongLong])
780             return [self WOTest_compareWithUnsignedLongLong:
781                 [aValue WOTest_unsignedLongLongValue]];
782         else if ([aValue WOTest_isFloat])
783             return [self WOTest_compareWithFloat:[aValue WOTest_floatValue]];
784         else if ([aValue WOTest_isDouble])
785             return [self WOTest_compareWithDouble:[aValue WOTest_doubleValue]];
786         else if ([aValue WOTest_isC99Bool])
787             return [self WOTest_compareWithC99Bool:[aValue WOTest_C99BoolValue]];
788     }
789
790     [NSException raise:NSInvalidArgumentException format:@"non-numeric value(s) passed"];
791
792     // never reached, but necessary to suppress compiler warning
793     return NSOrderedSame;
794 }
795
796 #pragma mark -
797 #pragma mark Utility methods
798
799 - (size_t)WOTest_bufferSize
800 {
801     return [[self class] WOTest_sizeForType:[self WOTest_objCTypeString]];
802 }
803
804 - (void)WOTest_printSignCompareWarning:(NSString *)warning
805 {
806     NSParameterAssert(warning != nil);
807
808     // this conditional in here so that the warnings can be turned off in WOTest self-testing
809     if ([[WOTest sharedInstance] warnsAboutSignComparisons])
810     {
811         [[WOTest sharedInstance] writeWarning:warning];
812         [[WOTest sharedInstance] writeLastKnownLocation];
813     }
814 }
815
816 #pragma mark -
817 #pragma mark Convenience methods
818
819 /*! Returns the Objective-C type of the receiver as an NSString. */
820 - (NSString *)WOTest_objCTypeString
821 {
822     return [NSString stringWithUTF8String:[self objCType]];
823 }
824
825 - (NSString *)WOTest_description
826 {
827     // these special handlings exist because NSValue's description is not very human-friendly
828     // it gets even worse when running on Intel: for example, an integer like 500,000 (0x0007a120 hex) is displayed as "<20a10700>"
829     if ([self WOTest_isObject])
830     {
831         // look for objects that are either (NSString objects) or objects that respond to "description"
832         id valueContents = [self WOTest_objectValue];
833         if (valueContents)
834         {
835             // for NSString objects: just return content
836             if ([NSObject WOTest_object:valueContents isKindOfClass:[NSString class]])
837                 return [[valueContents retain] autorelease];
838             else if ([NSObject WOTest_object:valueContents respondsToSelector:@selector(description)] &&
839                      [NSObject WOTest_isIdReturnType:[NSObject WOTest_returnTypeForObject:valueContents
840                                                                                  selector:@selector(description)]])
841             {
842                 NSString *description = objc_msgSend(valueContents, @selector(description));
843                 if (description && [NSObject WOTest_object:description isKindOfClass:[NSString class]])
844                     return description;
845             }
846         }
847     }
848     // TODO: write unit tests to confirm that NSString supports all of these printf format markers and modifiers
849     else if ([self WOTest_isChar])
850     {
851         char character = [self WOTest_charValue];
852         if (character == 32)                                                // the space character
853             return @"(space)";
854         else if ((character >= 33) && (character <= 126))                   // other printable characters printed as an ASCII char
855             return [NSString stringWithFormat:@"'%C'", (unichar)character];
856         else
857             return [NSString stringWithFormat:@"(char)%hhd", character];    // all others printed as signed numbers
858     }
859     else if ([self WOTest_isInt])
860         return [NSString stringWithFormat:@"(int)%d", [self WOTest_intValue]];
861     else if ([self WOTest_isShort])
862         return [NSString stringWithFormat:@"(short)%hi", [self WOTest_shortValue]];
863     else if ([self WOTest_isLong])
864         return [NSString stringWithFormat:@"(long)%ld", [self WOTest_longValue]];
865     else if ([self WOTest_isLongLong])
866         return [NSString stringWithFormat:@"(long long)%lld", [self WOTest_longLongValue]];
867     else if ([self WOTest_isUnsignedChar])
868     {
869         unsigned char character = [self WOTest_unsignedCharValue];
870         if (character == 32)                                        // the space character
871             return @"(space)";
872         else if ((character >= 33) && (character <= 126))           // other printable characters printed as an ASCII char
873             return [NSString stringWithFormat:@"%C", (unichar)character];
874         else
875             // all others printed as unsigned numbers
876             return [NSString stringWithFormat:@"(unsigned char)%hhu", [self WOTest_unsignedCharValue]];
877     }
878     else if ([self WOTest_isUnsignedInt])
879         return [NSString stringWithFormat:@"(unsigned int)%u", [self WOTest_unsignedIntValue]];
880     else if ([self WOTest_isUnsignedShort])
881         return [NSString stringWithFormat:@"(unsigned short)%hu", [self WOTest_unsignedShortValue]];
882     else if ([self WOTest_isUnsignedLong])
883         return [NSString stringWithFormat:@"(unsigned long)%lu", [self WOTest_unsignedLongValue]];
884     else if ([self WOTest_isUnsignedLongLong])
885         return [NSString stringWithFormat:@"(unsigned long long)%llu", [self WOTest_unsignedLongLongValue]];
886     else if ([self WOTest_isFloat])
887         return [NSString stringWithFormat:@"(float)%f", [self WOTest_floatValue]];
888     else if ([self WOTest_isDouble])
889         return [NSString stringWithFormat:@"(double)%f", [self WOTest_doubleValue]];
890     else if ([self WOTest_isC99Bool])
891         return [NSString stringWithFormat:@"(_Bool)%@", [self WOTest_C99BoolValue] ? @"true" : @"false"];
892     else if ([self WOTest_isVoid])
893         return @"(void)";
894     else if ([self WOTest_isConstantCharacterString])
895         return [NSString stringWithFormat:@"\"%s\"", [self WOTest_constantCharacterStringValue]];
896     else if ([self WOTest_isCharacterString])
897         return [NSString stringWithFormat:@"\"%s\"", [self WOTest_characterStringValue]];
898     else if ([self WOTest_isClass])
899         return [NSString stringWithFormat:@"(Class)%@", NSStringFromClass([self WOTest_classValue])];
900     else if ([self WOTest_isSelector])
901         return [NSString stringWithFormat:@"(SEL)%@", NSStringFromSelector([self WOTest_selectorValue])];
902     else if ([self WOTest_isPointerToVoid])
903         return [NSString stringWithFormat:@"(void *)%#08x", [self WOTest_pointerToVoidValue]];
904     return [self description];  // fallback case
905 }
906
907 #pragma mark -
908 #pragma mark Identifying generic types
909
910 - (BOOL)WOTest_isNumericScalar
911 {
912     return ([self WOTest_isChar]           || [self WOTest_isInt]                 ||
913             [self WOTest_isShort]          || [self WOTest_isLong]                ||
914             [self WOTest_isLongLong]       || [self WOTest_isUnsignedChar]        ||
915             [self WOTest_isUnsignedInt]    || [self WOTest_isUnsignedShort]       ||
916             [self WOTest_isUnsignedLong]   || [self WOTest_isUnsignedLongLong]    ||
917             [self WOTest_isFloat]          || [self WOTest_isDouble]              ||
918             [self WOTest_isC99Bool]);
919 }
920
921 - (BOOL)WOTest_isPointer
922 {
923     return [[self class] WOTest_typeIsPointer:[self WOTest_objCTypeString]];
924 }
925
926 - (BOOL)WOTest_isArray
927 {
928     return [[self class] WOTest_typeIsArray:[self WOTest_objCTypeString]];
929 }
930
931 - (unsigned)WOTest_arrayCount
932 {
933     NSAssert([self WOTest_isArray],
934              @"WOTest_arrayCount sent but receiver does not contain an array");
935     NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
936     unichar startMarker;
937     int count = 0;
938
939     // attempt the scan: it should work
940     if (!([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) && [scanner scanInt:&count]))
941         [NSException raise:NSInternalInconsistencyException format:@"scanner error in WOTest_arrayCount"];
942
943     return (unsigned)count;
944 }
945
946 - (NSString *)WOTest_arrayType
947 {
948     NSAssert([self WOTest_isArray], @"WOTest_arrayType sent but receiver does not contain an array");
949     NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
950     [scanner setScanLocation:1];
951     NSString *typeString;
952     if (!([scanner scanInt:nil] && [scanner WOTest_scanTypeIntoString:&typeString]))
953         [NSException raise:NSInternalInconsistencyException format:@"scanner error in WOTest_arrayType"];
954     return typeString;
955 }
956
957 - (BOOL)WOTest_isStruct
958 {
959     return [[self class] WOTest_typeIsStruct:[self WOTest_objCTypeString]];
960 }
961
962 - (BOOL)WOTest_isUnion
963 {
964     return [[self class] WOTest_typeIsUnion:[self WOTest_objCTypeString]];
965 }
966
967 - (BOOL)WOTest_isBitfield
968 {
969     return [[self class] WOTest_typeIsBitfield:[self WOTest_objCTypeString]];
970 }
971
972 - (BOOL)WOTest_isUnknown
973 {
974     return [[self class] WOTest_typeIsUnknown:[self WOTest_objCTypeString]];
975 }
976
977 #pragma mark -
978 #pragma mark Identifying and retrieving specific types
979
980 - (BOOL)WOTest_isChar
981 {
982     return [[self class] WOTest_typeIsChar:[self WOTest_objCTypeString]];
983 }
984
985 - (BOOL)WOTest_isInt
986 {
987     return [[self class] WOTest_typeIsInt:[self WOTest_objCTypeString]];
988 }
989
990 - (BOOL)WOTest_isShort
991 {
992     return [[self class] WOTest_typeIsShort:[self WOTest_objCTypeString]];
993 }
994
995 - (BOOL)WOTest_isLong
996 {
997     return [[self class] WOTest_typeIsLong:[self WOTest_objCTypeString]];
998 }
999
1000 - (BOOL)WOTest_isLongLong
1001 {
1002     return [[self class] WOTest_typeIsLongLong:[self WOTest_objCTypeString]];
1003 }
1004
1005 - (BOOL)WOTest_isUnsignedChar
1006 {
1007     return [[self class] WOTest_typeIsUnsignedChar:[self WOTest_objCTypeString]];
1008 }
1009
1010 - (BOOL)WOTest_isUnsignedInt
1011 {
1012     return [[self class] WOTest_typeIsUnsignedInt:[self WOTest_objCTypeString]];
1013 }
1014
1015 - (BOOL)WOTest_isUnsignedShort
1016 {
1017     return [[self class] WOTest_typeIsUnsignedShort:[self WOTest_objCTypeString]];
1018 }
1019
1020 - (BOOL)WOTest_isUnsignedLong
1021 {
1022     return [[self class] WOTest_typeIsUnsignedLong:[self WOTest_objCTypeString]];
1023 }
1024
1025 - (BOOL)WOTest_isUnsignedLongLong
1026 {
1027     return [[self class] WOTest_typeIsUnsignedLongLong:[self WOTest_objCTypeString]];
1028 }
1029
1030 - (BOOL)WOTest_isFloat
1031 {
1032     return [[self class] WOTest_typeIsFloat:[self WOTest_objCTypeString]];
1033 }
1034
1035 - (BOOL)WOTest_isDouble
1036 {
1037     return [[self class] WOTest_typeIsDouble:[self WOTest_objCTypeString]];
1038 }
1039
1040 - (BOOL)WOTest_isC99Bool
1041 {
1042     return [[self class] WOTest_typeIsC99Bool:[self WOTest_objCTypeString]];
1043 }
1044
1045 - (BOOL)WOTest_isVoid
1046 {
1047     return [[self class] WOTest_typeIsVoid:[self WOTest_objCTypeString]];
1048 }
1049
1050 - (BOOL)WOTest_isConstantCharacterString
1051 {
1052     return [[self class] WOTest_typeIsConstantCharacterString:[self WOTest_objCTypeString]];
1053 }
1054
1055 - (BOOL)WOTest_isCharacterString
1056 {
1057     return [[self class] WOTest_typeIsCharacterString:[self WOTest_objCTypeString]];
1058 }
1059
1060 - (BOOL)WOTest_isObject
1061 {
1062     return [[self class] WOTest_typeIsObject:[self WOTest_objCTypeString]];
1063 }
1064
1065 - (BOOL)WOTest_isClass
1066 {
1067     return [[self class] WOTest_typeIsClass:[self WOTest_objCTypeString]];
1068 }
1069
1070 - (BOOL)WOTest_isSelector
1071 {
1072     return [[self class] WOTest_typeIsSelector:[self WOTest_objCTypeString]];
1073 }
1074
1075 - (BOOL)WOTest_isPointerToVoid
1076 {
1077     return [[self class] WOTest_typeIsPointerToVoid:[self WOTest_objCTypeString]];
1078 }
1079
1080 - (char)WOTest_charValue
1081 {
1082     char value;
1083     [self getValue:&value];
1084     return value;
1085 }
1086
1087 - (int)WOTest_intValue
1088 {
1089     int value;
1090     [self getValue:&value];
1091     return value;
1092 }
1093
1094 - (short)WOTest_shortValue
1095 {
1096     short value;
1097     [self getValue:&value];
1098     return value;
1099 }
1100
1101 - (long)WOTest_longValue
1102 {
1103     long value;
1104     [self getValue:&value];
1105     return value;
1106 }
1107
1108 - (long long)WOTest_longLongValue
1109 {
1110     long long value;
1111     [self getValue:&value];
1112     return value;
1113 }
1114
1115 - (unsigned char)WOTest_unsignedCharValue
1116 {
1117     unsigned char value;
1118     [self getValue:&value];
1119     return value;
1120 }
1121
1122 - (unsigned int)WOTest_unsignedIntValue
1123 {
1124     unsigned int value;
1125     [self getValue:&value];
1126     return value;
1127 }
1128
1129 - (unsigned short)WOTest_unsignedShortValue
1130 {
1131     unsigned short value;
1132     [self getValue:&value];
1133     return value;
1134 }
1135
1136 - (unsigned long)WOTest_unsignedLongValue
1137 {
1138     unsigned long value;
1139     [self getValue:&value];
1140     return value;
1141 }
1142
1143 - (unsigned long long)WOTest_unsignedLongLongValue
1144 {
1145     unsigned long long value;
1146     [self getValue:&value];
1147     return value;
1148 }
1149
1150 - (float)WOTest_floatValue
1151 {
1152     float value;
1153     [self getValue:&value];
1154     return value;
1155 }
1156
1157 - (double)WOTest_doubleValue
1158 {
1159     double value;
1160     [self getValue:&value];
1161     return value;
1162 }
1163
1164 - (_Bool)WOTest_C99BoolValue
1165 {
1166     _Bool value;
1167     [self getValue:&value];
1168     return value;
1169 }
1170
1171 - (const char *)WOTest_constantCharacterStringValue
1172 {
1173     const char *value;
1174     [self getValue:&value];
1175     return value;
1176 }
1177
1178 - (char *)WOTest_characterStringValue
1179 {
1180     char *value;
1181     [self getValue:&value];
1182     return value;
1183 }
1184
1185 - (id)WOTest_objectValue
1186 {
1187     id value;
1188     [self getValue:&value];
1189     return value;
1190 }
1191
1192 - (Class)WOTest_classValue
1193 {
1194     Class value;
1195     [self getValue:&value];
1196     return value;
1197 }
1198
1199 - (SEL)WOTest_selectorValue
1200 {
1201     SEL value;
1202     [self getValue:&value];
1203     return value;
1204 }
1205
1206 - (void *)WOTest_pointerToVoidValue
1207 {
1208     void *value;
1209     [self getValue:&value];
1210     return value;
1211 }
1212
1213 - (BOOL)WOTest_isCharArray
1214 {
1215     // look for string of form "[4c]"
1216     NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
1217     unichar startMarker, flag, endMarker;
1218     int count;
1219     return ([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) &&
1220             [scanner scanInt:&count] &&
1221             [scanner WOTest_scanCharacter:&flag] && (flag == _C_CHR) &&
1222             [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
1223             [scanner isAtEnd]);
1224 }
1225
1226 - (NSString *)WOTest_stringValue
1227 {
1228     @try {
1229         if ([self WOTest_isCharacterString] || [self WOTest_isConstantCharacterString])
1230             return [NSString stringWithUTF8String:(const char *)[self pointerValue]];
1231         else // see if this is a char array
1232         {
1233             NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
1234             unichar startMarker, flag, endMarker;
1235             int count;
1236             if ([scanner WOTest_scanCharacter:&startMarker] &&
1237                 (startMarker == _C_ARY_B) && [scanner scanInt:&count] &&
1238                 [scanner WOTest_scanCharacter:&flag] && (flag == _C_CHR) &&
1239                 [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
1240                 [scanner isAtEnd])
1241             {
1242                 // is char array
1243                 if (count > 0)
1244                 {
1245                     char *buffer = malloc(count * sizeof(char));
1246                     NSAssert1(buffer != NULL, @"malloc() failed (size %d)",
1247                               (count * sizeof(char)));
1248                     [self getValue:buffer];
1249
1250                     // confirm that this is a null-terminated string
1251                     for (int i = 0; i < count; i++)
1252                     {
1253                         if (buffer[i] == 0)
1254                             return [NSString stringWithUTF8String:buffer];
1255                     }
1256                     free(buffer);
1257                 }
1258             }
1259         }
1260     }
1261     @catch (id e) {
1262         // fall through
1263     }
1264     return nil;
1265 }
1266
1267 #pragma mark -
1268 #pragma mark Low-level test methods
1269
1270 /* Unfortunately there is a lot of very similar code repeated across these methods but it seems to be a necessary evil (600 lines of necessary evil). Firstly, it's necessary to explicitly declare the type of the right-hand value of the comparison. There are lots of permuations for implicit casts, explicit casts (and warnings), and GCC seems to warn about signed to unsigned comparisons differently depending on the types. */
1271 - (NSComparisonResult)WOTest_compareWithChar:(char)other
1272 {
1273     if ([self WOTest_isChar]) // (also BOOL)
1274         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // no cast
1275     else if ([self WOTest_isInt])
1276         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1277     else if ([self WOTest_isShort])
1278         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1279     else if ([self WOTest_isLong])
1280         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1281     else if ([self WOTest_isLongLong])
1282         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1283     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1284         // implicit cast
1285         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1286     else if ([self WOTest_isUnsignedInt])
1287     {
1288         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1289         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned char)other); // explicit cast
1290     }
1291     else if ([self WOTest_isUnsignedShort])
1292         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1293     else if ([self WOTest_isUnsignedLong])
1294     {
1295         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1296         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned char)other); // explicit cast
1297     }
1298     else if ([self WOTest_isUnsignedLongLong])
1299     {
1300         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1301         // explicit cast
1302         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned char)other);
1303     }
1304     else if ([self WOTest_isFloat])
1305         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1306     else if ([self WOTest_isDouble])
1307          return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1308     else if ([self WOTest_isC99Bool])
1309         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1310
1311     // all other cases
1312     [NSException raise:NSInvalidArgumentException
1313                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1314
1315     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1316 }
1317
1318 - (NSComparisonResult)WOTest_compareWithInt:(int)other
1319 {
1320     if ([self WOTest_isChar]) // (also BOOL)
1321         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1322     else if ([self WOTest_isInt])
1323         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // no cast
1324     else if ([self WOTest_isShort])
1325         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1326     else if ([self WOTest_isLong])
1327         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1328     else if ([self WOTest_isLongLong])
1329         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1330     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1331                                     // implicit cast
1332         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1333     else if ([self WOTest_isUnsignedInt])
1334     {
1335         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1336         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned int)other); // explicit cast
1337     }
1338     else if ([self WOTest_isUnsignedShort])
1339         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1340     else if ([self WOTest_isUnsignedLong])
1341     {
1342         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1343         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned int)other); // explicit cast
1344     }
1345     else if ([self WOTest_isUnsignedLongLong])
1346     {
1347         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1348         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned int)other);  // explicit cast
1349     }
1350     else if ([self WOTest_isFloat])
1351         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1352     else if ([self WOTest_isDouble])
1353         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1354     else if ([self WOTest_isC99Bool])
1355         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1356
1357     // all other cases
1358     [NSException raise:NSInvalidArgumentException
1359                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1360
1361     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1362 }
1363
1364 - (NSComparisonResult)WOTest_compareWithShort:(short)other
1365 {
1366     if ([self WOTest_isChar]) // (also BOOL)
1367         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1368     else if ([self WOTest_isInt])
1369         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1370     else if ([self WOTest_isShort])
1371         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // no cast
1372     else if ([self WOTest_isLong])
1373         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1374     else if ([self WOTest_isLongLong])
1375         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1376     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1377                                     // implicit cast
1378         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1379     else if ([self WOTest_isUnsignedInt])
1380     {
1381         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1382         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned short)other); // explicit cast
1383     }
1384     else if ([self WOTest_isUnsignedShort])
1385         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1386     else if ([self WOTest_isUnsignedLong])
1387     {
1388         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1389         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned short)other); // explicit cast
1390     }
1391     else if ([self WOTest_isUnsignedLongLong])
1392     {
1393         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1394         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned short)other);  // explicit cast
1395     }
1396     else if ([self WOTest_isFloat])
1397         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1398     else if ([self WOTest_isDouble])
1399         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1400     else if ([self WOTest_isC99Bool])
1401         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1402
1403     // all other cases
1404     [NSException raise:NSInvalidArgumentException
1405                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1406
1407     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1408 }
1409
1410 - (NSComparisonResult)WOTest_compareWithLong:(long)other
1411 {
1412     if ([self WOTest_isChar]) // (also BOOL)
1413         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1414     else if ([self WOTest_isInt])
1415         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1416     else if ([self WOTest_isShort])
1417         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1418     else if ([self WOTest_isLong])
1419         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // no cast
1420     else if ([self WOTest_isLongLong])
1421         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1422     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1423                                     // implicit cast
1424         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1425     else if ([self WOTest_isUnsignedInt])
1426     {
1427         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1428         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned long)other); // explicit cast
1429     }
1430     else if ([self WOTest_isUnsignedShort])
1431         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1432     else if ([self WOTest_isUnsignedLong])
1433     {
1434         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1435         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned long)other); // explicit cast
1436     }
1437     else if ([self WOTest_isUnsignedLongLong])
1438     {
1439         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1440         // explicit cast
1441         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned long)other);
1442     }
1443     else if ([self WOTest_isFloat])
1444         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1445     else if ([self WOTest_isDouble])
1446         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1447     else if ([self WOTest_isC99Bool])
1448         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1449
1450     // all other cases
1451     [NSException raise:NSInvalidArgumentException
1452                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1453
1454     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1455 }
1456
1457 - (NSComparisonResult)WOTest_compareWithLongLong:(long long)other
1458 {
1459     if ([self WOTest_isChar]) // (also BOOL)
1460         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1461     else if ([self WOTest_isInt])
1462         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1463     else if ([self WOTest_isShort])
1464         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1465     else if ([self WOTest_isLong])
1466         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1467     else if ([self WOTest_isLongLong])
1468         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // no cast
1469     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1470         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1471     else if ([self WOTest_isUnsignedInt])
1472         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1473     else if ([self WOTest_isUnsignedShort])
1474         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1475     else if ([self WOTest_isUnsignedLong])
1476         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1477     else if ([self WOTest_isUnsignedLongLong])
1478     {
1479         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1480         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned long long)other);  // explicit cast
1481     }
1482     else if ([self WOTest_isFloat])
1483         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1484     else if ([self WOTest_isDouble])
1485         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1486     else if ([self WOTest_isC99Bool])
1487         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1488
1489     // all other cases
1490     [NSException raise:NSInvalidArgumentException
1491                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1492
1493     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1494 }
1495
1496 - (NSComparisonResult)WOTest_compareWithUnsignedChar:(unsigned char)other
1497 {
1498     if ([self WOTest_isChar]) // (also BOOL)
1499         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1500     else if ([self WOTest_isInt])
1501         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1502     else if ([self WOTest_isShort])
1503         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1504     else if ([self WOTest_isLong])
1505         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1506     else if ([self WOTest_isLongLong])
1507         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1508     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1509         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // no cast
1510     else if ([self WOTest_isUnsignedInt])
1511         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1512     else if ([self WOTest_isUnsignedShort])
1513         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1514     else if ([self WOTest_isUnsignedLong])
1515         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1516     else if ([self WOTest_isUnsignedLongLong])
1517         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1518     else if ([self WOTest_isFloat])
1519         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1520     else if ([self WOTest_isDouble])
1521         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1522     else if ([self WOTest_isC99Bool])
1523         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1524
1525     // all other cases
1526     [NSException raise:NSInvalidArgumentException
1527                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1528
1529     return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
1530 }
1531
1532 - (NSComparisonResult)WOTest_compareWithUnsignedInt:(unsigned int)other
1533 {
1534     if ([self WOTest_isChar]) // (also BOOL)
1535     {
1536         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1537         return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // explicit cast
1538     }
1539     else if ([self WOTest_isInt])
1540     {
1541         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1542         return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
1543     }
1544     else if ([self WOTest_isShort])
1545     {
1546         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1547         return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
1548     }
1549     else if ([self WOTest_isLong])
1550     {
1551         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1552         return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
1553     }
1554     else if ([self WOTest_isLongLong])
1555         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1556     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1557         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1558     else if ([self WOTest_isUnsignedInt])
1559         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // no cast
1560     else if ([self WOTest_isUnsignedShort])
1561         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1562     else if ([self WOTest_isUnsignedLong])
1563         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1564     else if ([self WOTest_isUnsignedLongLong])
1565         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1566     else if ([self WOTest_isFloat])
1567         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1568     else if ([self WOTest_isDouble])
1569         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1570     else if ([self WOTest_isC99Bool])
1571         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1572
1573     // all other cases
1574     [NSException raise:NSInvalidArgumentException
1575                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1576
1577     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1578 }
1579
1580 - (NSComparisonResult)WOTest_compareWithUnsignedShort:(unsigned short)other
1581 {
1582     if ([self WOTest_isChar]) // (also BOOL)
1583         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1584     else if ([self WOTest_isInt])
1585         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1586     else if ([self WOTest_isShort])
1587         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1588     else if ([self WOTest_isLong])
1589         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1590     else if ([self WOTest_isLongLong])
1591         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1592     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1593         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1594     else if ([self WOTest_isUnsignedInt])
1595         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1596     else if ([self WOTest_isUnsignedShort])
1597         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // no cast
1598     else if ([self WOTest_isUnsignedLong])
1599         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1600     else if ([self WOTest_isUnsignedLongLong])
1601         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1602     else if ([self WOTest_isFloat])
1603         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1604     else if ([self WOTest_isDouble])
1605         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1606     else if ([self WOTest_isC99Bool])
1607         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1608
1609     // all other cases
1610     [NSException raise:NSInvalidArgumentException
1611                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1612
1613     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1614 }
1615
1616 - (NSComparisonResult)WOTest_compareWithUnsignedLong:(unsigned long)other
1617 {
1618     if ([self WOTest_isChar]) // char (also BOOL)
1619     {
1620         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1621         return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // expicit cast
1622     }
1623     else if ([self WOTest_isInt]) // int
1624     {
1625         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1626         return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
1627     }
1628     else if ([self WOTest_isShort]) // short
1629     {
1630         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1631         return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
1632     }
1633     else if ([self WOTest_isLong]) // long
1634     {
1635         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1636         return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
1637     }
1638     else if ([self WOTest_isLongLong]) // long long
1639         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1640     else if ([self WOTest_isUnsignedChar]) // unsigned char (also Boolean)
1641         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1642     else if ([self WOTest_isUnsignedInt]) // unsigned int
1643         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // implicit cast
1644     else if ([self WOTest_isUnsignedShort]) // unsigned short
1645         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1646     else if ([self WOTest_isUnsignedLong]) // unsigned long
1647         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // no cast
1648     else if ([self WOTest_isUnsignedLongLong]) // unsigned long long
1649         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1650     else if ([self WOTest_isFloat]) // float
1651         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1652     else if ([self WOTest_isDouble]) // double
1653         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1654     else if ([self WOTest_isC99Bool]) // C99 _Bool
1655         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1656
1657     // all other cases
1658     [NSException raise:NSInvalidArgumentException
1659                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1660
1661     return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
1662 }
1663
1664 - (NSComparisonResult)WOTest_compareWithUnsignedLongLong:(unsigned long long)other
1665 {
1666     if ([self WOTest_isChar]) // (also BOOL)
1667     {
1668         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1669         return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // explicit cast
1670     }
1671     else if ([self WOTest_isInt])
1672     {
1673         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1674         return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
1675     }
1676     else if ([self WOTest_isShort])
1677     {
1678         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1679         return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
1680     }
1681     else if ([self WOTest_isLong])
1682     {
1683         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1684         return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
1685     }
1686     else if ([self WOTest_isLongLong])
1687     {
1688         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1689         return WO_COMPARE_SCALARS((unsigned long long)[self WOTest_longLongValue], other); // explicit cast
1690     }
1691     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1692         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1693     else if ([self WOTest_isUnsignedInt])
1694         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // implicit cast
1695     else if ([self WOTest_isUnsignedShort])
1696         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1697     else if ([self WOTest_isUnsignedLong])
1698         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1699     else if ([self WOTest_isUnsignedLongLong])
1700         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // no cast
1701     else if ([self WOTest_isFloat])
1702         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1703     else if ([self WOTest_isDouble])
1704         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1705     else if ([self WOTest_isC99Bool])
1706         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1707
1708     // all other cases
1709     [NSException raise:NSInvalidArgumentException
1710                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1711
1712     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1713 }
1714
1715 - (NSComparisonResult)WOTest_compareWithFloat:(float)other
1716 {
1717     if ([self WOTest_isChar]) // (also BOOL)
1718         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1719     else if ([self WOTest_isInt])
1720         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1721     else if ([self WOTest_isShort])
1722         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1723     else if ([self WOTest_isLong])
1724         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1725     else if ([self WOTest_isLongLong])
1726         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1727     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1728         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1729     else if ([self WOTest_isUnsignedInt])
1730         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1731     else if ([self WOTest_isUnsignedShort])
1732         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1733     else if ([self WOTest_isUnsignedLong])
1734         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1735     else if ([self WOTest_isUnsignedLongLong])
1736         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1737     else if ([self WOTest_isFloat])
1738         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // no cast
1739     else if ([self WOTest_isDouble])
1740         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1741     else if ([self WOTest_isC99Bool])
1742         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1743
1744     // all other cases
1745     [NSException raise:NSInvalidArgumentException
1746                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1747
1748     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1749 }
1750
1751 - (NSComparisonResult)WOTest_compareWithDouble:(double)other
1752 {
1753     if ([self WOTest_isChar]) // (also BOOL)
1754         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1755     else if ([self WOTest_isInt])
1756         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1757     else if ([self WOTest_isShort])
1758         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1759     else if ([self WOTest_isLong])
1760         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1761     else if ([self WOTest_isLongLong])
1762         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1763     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1764         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1765     else if ([self WOTest_isUnsignedInt])
1766         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1767     else if ([self WOTest_isUnsignedShort])
1768         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1769     else if ([self WOTest_isUnsignedLong])
1770         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1771     else if ([self WOTest_isUnsignedLongLong])
1772         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1773     else if ([self WOTest_isFloat])
1774         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1775     else if ([self WOTest_isDouble])
1776         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // no cast
1777     else if ([self WOTest_isC99Bool])
1778         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1779
1780     // all other cases
1781     [NSException raise:NSInvalidArgumentException
1782                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1783
1784     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1785 }
1786
1787 - (NSComparisonResult)WOTest_compareWithC99Bool:(_Bool)other
1788 {
1789     if ([self WOTest_isChar]) // (also BOOL)
1790         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1791     else if ([self WOTest_isInt])
1792         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1793     else if ([self WOTest_isShort])
1794         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1795     else if ([self WOTest_isLong])
1796         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1797     else if ([self WOTest_isLongLong])
1798         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1799     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1800         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1801     else if ([self WOTest_isUnsignedInt])
1802         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1803     else if ([self WOTest_isUnsignedShort])
1804         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1805     else if ([self WOTest_isUnsignedLong])
1806         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1807     else if ([self WOTest_isUnsignedLongLong])
1808         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1809     else if ([self WOTest_isFloat])
1810         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1811     else if ([self WOTest_isDouble])
1812         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1813     else if ([self WOTest_isC99Bool])
1814         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // no cast
1815
1816     // all other cases
1817     [NSException raise:NSInvalidArgumentException
1818                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1819
1820     return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
1821 }
1822
1823 @end