]> git.wincent.com - WOTest.git/blob - NSValue+WOTest.m
Code clean-up for garbage collection
[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     // pointer-to-void case
759     if ([self WOTest_isPointerToVoid] && [aValue WOTest_isPointerToVoid])
760     {
761         // test conservatively here: equal pointers are considered equal;
762         // all others fall through and an exception is raised
763         if ([self pointerValue] == [aValue pointerValue])
764             return NSOrderedSame;
765     }
766
767     // numeric scalar case
768     if ([self WOTest_isNumericScalar] && [aValue WOTest_isNumericScalar])
769     {
770         if ([aValue WOTest_isChar])
771             return [self WOTest_compareWithChar:[aValue WOTest_charValue]];
772         else if ([aValue WOTest_isInt])
773             return [self WOTest_compareWithInt:[aValue WOTest_intValue]];
774         else if ([aValue WOTest_isShort])
775             return [self WOTest_compareWithShort:[aValue WOTest_shortValue]];
776         else if ([aValue WOTest_isLong])
777             return [self WOTest_compareWithLong:[aValue WOTest_longValue]];
778         else if ([aValue WOTest_isLongLong])
779             return [self WOTest_compareWithLongLong:[aValue WOTest_longLongValue]];
780         else if ([aValue WOTest_isUnsignedChar])
781             return [self WOTest_compareWithUnsignedChar:[aValue WOTest_unsignedCharValue]];
782         else if ([aValue WOTest_isUnsignedInt])
783             return [self WOTest_compareWithUnsignedInt:[aValue WOTest_unsignedIntValue]];
784         else if ([aValue WOTest_isUnsignedShort])
785             return [self WOTest_compareWithUnsignedShort:[aValue WOTest_unsignedShortValue]];
786         else if ([aValue WOTest_isUnsignedLong])
787             return [self WOTest_compareWithUnsignedLong:[aValue WOTest_unsignedLongValue]];
788         else if ([aValue WOTest_isUnsignedLongLong])
789             return [self WOTest_compareWithUnsignedLongLong:
790                 [aValue WOTest_unsignedLongLongValue]];
791         else if ([aValue WOTest_isFloat])
792             return [self WOTest_compareWithFloat:[aValue WOTest_floatValue]];
793         else if ([aValue WOTest_isDouble])
794             return [self WOTest_compareWithDouble:[aValue WOTest_doubleValue]];
795         else if ([aValue WOTest_isC99Bool])
796             return [self WOTest_compareWithC99Bool:[aValue WOTest_C99BoolValue]];
797     }
798
799     [NSException raise:NSInvalidArgumentException format:@"non-numeric value(s) passed"];
800
801     // never reached, but necessary to suppress compiler warning
802     return NSOrderedSame;
803 }
804
805 #pragma mark -
806 #pragma mark Utility methods
807
808 - (size_t)WOTest_bufferSize
809 {
810     return [[self class] WOTest_sizeForType:[self WOTest_objCTypeString]];
811 }
812
813 - (void)WOTest_printSignCompareWarning:(NSString *)warning
814 {
815     NSParameterAssert(warning != nil);
816
817     // this conditional in here so that the warnings can be turned off in WOTest self-testing
818     if ([[WOTest sharedInstance] warnsAboutSignComparisons])
819     {
820         [[WOTest sharedInstance] writeWarning:warning];
821         [[WOTest sharedInstance] writeLastKnownLocation];
822     }
823 }
824
825 #pragma mark -
826 #pragma mark Convenience methods
827
828 /*! Returns the Objective-C type of the receiver as an NSString. */
829 - (NSString *)WOTest_objCTypeString
830 {
831     return [NSString stringWithUTF8String:[self objCType]];
832 }
833
834 - (NSString *)WOTest_description
835 {
836     // these special handlings exist because NSValue's description is not very human-friendly
837     // it gets even worse when running on Intel: for example, an integer like 500,000 (0x0007a120 hex) is displayed as "<20a10700>"
838     if ([self WOTest_isObject])
839     {
840         // look for objects that are either (NSString objects) or objects that respond to "description"
841         id valueContents = [self WOTest_objectValue];
842         if (valueContents)
843         {
844             // for NSString objects: just return content
845             if ([NSObject WOTest_object:valueContents isKindOfClass:[NSString class]])
846                 return valueContents;
847             else if ([NSObject WOTest_object:valueContents respondsToSelector:@selector(description)] &&
848                      [NSObject WOTest_isIdReturnType:[NSObject WOTest_returnTypeForObject:valueContents
849                                                                                  selector:@selector(description)]])
850             {
851                 NSString *description = objc_msgSend(valueContents, @selector(description));
852                 if (description && [NSObject WOTest_object:description isKindOfClass:[NSString class]])
853                     return description;
854             }
855         }
856     }
857     // TODO: write unit tests to confirm that NSString supports all of these printf format markers and modifiers
858     else if ([self WOTest_isChar])
859     {
860         char character = [self WOTest_charValue];
861         if (character == 32)                                                // the space character
862             return @"(space)";
863         else if ((character >= 33) && (character <= 126))                   // other printable characters printed as an ASCII char
864             return [NSString stringWithFormat:@"'%C'", (unichar)character];
865         else
866             return [NSString stringWithFormat:@"(char)%hhd", character];    // all others printed as signed numbers
867     }
868     else if ([self WOTest_isInt])
869         return [NSString stringWithFormat:@"(int)%d", [self WOTest_intValue]];
870     else if ([self WOTest_isShort])
871         return [NSString stringWithFormat:@"(short)%hi", [self WOTest_shortValue]];
872     else if ([self WOTest_isLong])
873         return [NSString stringWithFormat:@"(long)%ld", [self WOTest_longValue]];
874     else if ([self WOTest_isLongLong])
875         return [NSString stringWithFormat:@"(long long)%lld", [self WOTest_longLongValue]];
876     else if ([self WOTest_isUnsignedChar])
877     {
878         unsigned char character = [self WOTest_unsignedCharValue];
879         if (character == 32)                                        // the space character
880             return @"(space)";
881         else if ((character >= 33) && (character <= 126))           // other printable characters printed as an ASCII char
882             return [NSString stringWithFormat:@"%C", (unichar)character];
883         else
884             // all others printed as unsigned numbers
885             return [NSString stringWithFormat:@"(unsigned char)%hhu", [self WOTest_unsignedCharValue]];
886     }
887     else if ([self WOTest_isUnsignedInt])
888         return [NSString stringWithFormat:@"(unsigned int)%u", [self WOTest_unsignedIntValue]];
889     else if ([self WOTest_isUnsignedShort])
890         return [NSString stringWithFormat:@"(unsigned short)%hu", [self WOTest_unsignedShortValue]];
891     else if ([self WOTest_isUnsignedLong])
892         return [NSString stringWithFormat:@"(unsigned long)%lu", [self WOTest_unsignedLongValue]];
893     else if ([self WOTest_isUnsignedLongLong])
894         return [NSString stringWithFormat:@"(unsigned long long)%llu", [self WOTest_unsignedLongLongValue]];
895     else if ([self WOTest_isFloat])
896         return [NSString stringWithFormat:@"(float)%f", [self WOTest_floatValue]];
897     else if ([self WOTest_isDouble])
898         return [NSString stringWithFormat:@"(double)%f", [self WOTest_doubleValue]];
899     else if ([self WOTest_isC99Bool])
900         return [NSString stringWithFormat:@"(_Bool)%@", [self WOTest_C99BoolValue] ? @"true" : @"false"];
901     else if ([self WOTest_isVoid])
902         return @"(void)";
903     else if ([self WOTest_isConstantCharacterString])
904         return [NSString stringWithFormat:@"\"%s\"", [self WOTest_constantCharacterStringValue]];
905     else if ([self WOTest_isCharacterString])
906         return [NSString stringWithFormat:@"\"%s\"", [self WOTest_characterStringValue]];
907     else if ([self WOTest_isClass])
908         return [NSString stringWithFormat:@"(Class)%@", NSStringFromClass([self WOTest_classValue])];
909     else if ([self WOTest_isSelector])
910         return [NSString stringWithFormat:@"(SEL)%@", NSStringFromSelector([self WOTest_selectorValue])];
911     else if ([self WOTest_isPointerToVoid])
912         return [NSString stringWithFormat:@"(void *)%#08x", [self WOTest_pointerToVoidValue]];
913     return [self description];  // fallback case
914 }
915
916 #pragma mark -
917 #pragma mark Identifying generic types
918
919 - (BOOL)WOTest_isNumericScalar
920 {
921     return ([self WOTest_isChar]           || [self WOTest_isInt]                 ||
922             [self WOTest_isShort]          || [self WOTest_isLong]                ||
923             [self WOTest_isLongLong]       || [self WOTest_isUnsignedChar]        ||
924             [self WOTest_isUnsignedInt]    || [self WOTest_isUnsignedShort]       ||
925             [self WOTest_isUnsignedLong]   || [self WOTest_isUnsignedLongLong]    ||
926             [self WOTest_isFloat]          || [self WOTest_isDouble]              ||
927             [self WOTest_isC99Bool]);
928 }
929
930 - (BOOL)WOTest_isPointer
931 {
932     return [[self class] WOTest_typeIsPointer:[self WOTest_objCTypeString]];
933 }
934
935 - (BOOL)WOTest_isArray
936 {
937     return [[self class] WOTest_typeIsArray:[self WOTest_objCTypeString]];
938 }
939
940 - (unsigned)WOTest_arrayCount
941 {
942     NSAssert([self WOTest_isArray],
943              @"WOTest_arrayCount sent but receiver does not contain an array");
944     NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
945     unichar startMarker;
946     int count = 0;
947
948     // attempt the scan: it should work
949     if (!([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) && [scanner scanInt:&count]))
950         [NSException raise:NSInternalInconsistencyException format:@"scanner error in WOTest_arrayCount"];
951
952     return (unsigned)count;
953 }
954
955 - (NSString *)WOTest_arrayType
956 {
957     NSAssert([self WOTest_isArray], @"WOTest_arrayType sent but receiver does not contain an array");
958     NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
959     [scanner setScanLocation:1];
960     NSString *typeString;
961     if (!([scanner scanInt:nil] && [scanner WOTest_scanTypeIntoString:&typeString]))
962         [NSException raise:NSInternalInconsistencyException format:@"scanner error in WOTest_arrayType"];
963     return typeString;
964 }
965
966 - (BOOL)WOTest_isStruct
967 {
968     return [[self class] WOTest_typeIsStruct:[self WOTest_objCTypeString]];
969 }
970
971 - (BOOL)WOTest_isUnion
972 {
973     return [[self class] WOTest_typeIsUnion:[self WOTest_objCTypeString]];
974 }
975
976 - (BOOL)WOTest_isBitfield
977 {
978     return [[self class] WOTest_typeIsBitfield:[self WOTest_objCTypeString]];
979 }
980
981 - (BOOL)WOTest_isUnknown
982 {
983     return [[self class] WOTest_typeIsUnknown:[self WOTest_objCTypeString]];
984 }
985
986 #pragma mark -
987 #pragma mark Identifying and retrieving specific types
988
989 - (BOOL)WOTest_isChar
990 {
991     return [[self class] WOTest_typeIsChar:[self WOTest_objCTypeString]];
992 }
993
994 - (BOOL)WOTest_isInt
995 {
996     return [[self class] WOTest_typeIsInt:[self WOTest_objCTypeString]];
997 }
998
999 - (BOOL)WOTest_isShort
1000 {
1001     return [[self class] WOTest_typeIsShort:[self WOTest_objCTypeString]];
1002 }
1003
1004 - (BOOL)WOTest_isLong
1005 {
1006     return [[self class] WOTest_typeIsLong:[self WOTest_objCTypeString]];
1007 }
1008
1009 - (BOOL)WOTest_isLongLong
1010 {
1011     return [[self class] WOTest_typeIsLongLong:[self WOTest_objCTypeString]];
1012 }
1013
1014 - (BOOL)WOTest_isUnsignedChar
1015 {
1016     return [[self class] WOTest_typeIsUnsignedChar:[self WOTest_objCTypeString]];
1017 }
1018
1019 - (BOOL)WOTest_isUnsignedInt
1020 {
1021     return [[self class] WOTest_typeIsUnsignedInt:[self WOTest_objCTypeString]];
1022 }
1023
1024 - (BOOL)WOTest_isUnsignedShort
1025 {
1026     return [[self class] WOTest_typeIsUnsignedShort:[self WOTest_objCTypeString]];
1027 }
1028
1029 - (BOOL)WOTest_isUnsignedLong
1030 {
1031     return [[self class] WOTest_typeIsUnsignedLong:[self WOTest_objCTypeString]];
1032 }
1033
1034 - (BOOL)WOTest_isUnsignedLongLong
1035 {
1036     return [[self class] WOTest_typeIsUnsignedLongLong:[self WOTest_objCTypeString]];
1037 }
1038
1039 - (BOOL)WOTest_isFloat
1040 {
1041     return [[self class] WOTest_typeIsFloat:[self WOTest_objCTypeString]];
1042 }
1043
1044 - (BOOL)WOTest_isDouble
1045 {
1046     return [[self class] WOTest_typeIsDouble:[self WOTest_objCTypeString]];
1047 }
1048
1049 - (BOOL)WOTest_isC99Bool
1050 {
1051     return [[self class] WOTest_typeIsC99Bool:[self WOTest_objCTypeString]];
1052 }
1053
1054 - (BOOL)WOTest_isVoid
1055 {
1056     return [[self class] WOTest_typeIsVoid:[self WOTest_objCTypeString]];
1057 }
1058
1059 - (BOOL)WOTest_isConstantCharacterString
1060 {
1061     return [[self class] WOTest_typeIsConstantCharacterString:[self WOTest_objCTypeString]];
1062 }
1063
1064 - (BOOL)WOTest_isCharacterString
1065 {
1066     return [[self class] WOTest_typeIsCharacterString:[self WOTest_objCTypeString]];
1067 }
1068
1069 - (BOOL)WOTest_isObject
1070 {
1071     return [[self class] WOTest_typeIsObject:[self WOTest_objCTypeString]];
1072 }
1073
1074 - (BOOL)WOTest_isClass
1075 {
1076     return [[self class] WOTest_typeIsClass:[self WOTest_objCTypeString]];
1077 }
1078
1079 - (BOOL)WOTest_isSelector
1080 {
1081     return [[self class] WOTest_typeIsSelector:[self WOTest_objCTypeString]];
1082 }
1083
1084 - (BOOL)WOTest_isPointerToVoid
1085 {
1086     return [[self class] WOTest_typeIsPointerToVoid:[self WOTest_objCTypeString]];
1087 }
1088
1089 - (char)WOTest_charValue
1090 {
1091     char value;
1092     [self getValue:&value];
1093     return value;
1094 }
1095
1096 - (int)WOTest_intValue
1097 {
1098     int value;
1099     [self getValue:&value];
1100     return value;
1101 }
1102
1103 - (short)WOTest_shortValue
1104 {
1105     short value;
1106     [self getValue:&value];
1107     return value;
1108 }
1109
1110 - (long)WOTest_longValue
1111 {
1112     long value;
1113     [self getValue:&value];
1114     return value;
1115 }
1116
1117 - (long long)WOTest_longLongValue
1118 {
1119     long long value;
1120     [self getValue:&value];
1121     return value;
1122 }
1123
1124 - (unsigned char)WOTest_unsignedCharValue
1125 {
1126     unsigned char value;
1127     [self getValue:&value];
1128     return value;
1129 }
1130
1131 - (unsigned int)WOTest_unsignedIntValue
1132 {
1133     unsigned int value;
1134     [self getValue:&value];
1135     return value;
1136 }
1137
1138 - (unsigned short)WOTest_unsignedShortValue
1139 {
1140     unsigned short value;
1141     [self getValue:&value];
1142     return value;
1143 }
1144
1145 - (unsigned long)WOTest_unsignedLongValue
1146 {
1147     unsigned long value;
1148     [self getValue:&value];
1149     return value;
1150 }
1151
1152 - (unsigned long long)WOTest_unsignedLongLongValue
1153 {
1154     unsigned long long value;
1155     [self getValue:&value];
1156     return value;
1157 }
1158
1159 - (float)WOTest_floatValue
1160 {
1161     float value;
1162     [self getValue:&value];
1163     return value;
1164 }
1165
1166 - (double)WOTest_doubleValue
1167 {
1168     double value;
1169     [self getValue:&value];
1170     return value;
1171 }
1172
1173 - (_Bool)WOTest_C99BoolValue
1174 {
1175     _Bool value;
1176     [self getValue:&value];
1177     return value;
1178 }
1179
1180 - (const char *)WOTest_constantCharacterStringValue
1181 {
1182     const char *value;
1183     [self getValue:&value];
1184     return value;
1185 }
1186
1187 - (char *)WOTest_characterStringValue
1188 {
1189     char *value;
1190     [self getValue:&value];
1191     return value;
1192 }
1193
1194 - (id)WOTest_objectValue
1195 {
1196     id value;
1197     [self getValue:&value];
1198     return value;
1199 }
1200
1201 - (Class)WOTest_classValue
1202 {
1203     Class value;
1204     [self getValue:&value];
1205     return value;
1206 }
1207
1208 - (SEL)WOTest_selectorValue
1209 {
1210     SEL value;
1211     [self getValue:&value];
1212     return value;
1213 }
1214
1215 - (void *)WOTest_pointerToVoidValue
1216 {
1217     void *value;
1218     [self getValue:&value];
1219     return value;
1220 }
1221
1222 - (BOOL)WOTest_isCharArray
1223 {
1224     // look for string of form "[4c]"
1225     NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
1226     unichar startMarker, flag, endMarker;
1227     int count;
1228     return ([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) &&
1229             [scanner scanInt:&count] &&
1230             [scanner WOTest_scanCharacter:&flag] && (flag == _C_CHR) &&
1231             [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
1232             [scanner isAtEnd]);
1233 }
1234
1235 - (NSString *)WOTest_stringValue
1236 {
1237     @try {
1238         if ([self WOTest_isCharacterString] || [self WOTest_isConstantCharacterString])
1239             return [NSString stringWithUTF8String:(const char *)[self pointerValue]];
1240         else // see if this is a char array
1241         {
1242             NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
1243             unichar startMarker, flag, endMarker;
1244             int count;
1245             if ([scanner WOTest_scanCharacter:&startMarker] &&
1246                 (startMarker == _C_ARY_B) && [scanner scanInt:&count] &&
1247                 [scanner WOTest_scanCharacter:&flag] && (flag == _C_CHR) &&
1248                 [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
1249                 [scanner isAtEnd])
1250             {
1251                 // is char array
1252                 if (count > 0)
1253                 {
1254                     char *buffer = malloc(count * sizeof(char));
1255                     NSAssert1(buffer != NULL, @"malloc() failed (size %d)",
1256                               (count * sizeof(char)));
1257                     [self getValue:buffer];
1258
1259                     // confirm that this is a null-terminated string
1260                     for (int i = 0; i < count; i++)
1261                     {
1262                         if (buffer[i] == 0)
1263                             return [NSString stringWithUTF8String:buffer];
1264                     }
1265                     free(buffer);
1266                 }
1267             }
1268         }
1269     }
1270     @catch (id e) {
1271         // fall through
1272     }
1273     return nil;
1274 }
1275
1276 #pragma mark -
1277 #pragma mark Low-level test methods
1278
1279 /* 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. */
1280 - (NSComparisonResult)WOTest_compareWithChar:(char)other
1281 {
1282     if ([self WOTest_isChar]) // (also BOOL)
1283         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // no cast
1284     else if ([self WOTest_isInt])
1285         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1286     else if ([self WOTest_isShort])
1287         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1288     else if ([self WOTest_isLong])
1289         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1290     else if ([self WOTest_isLongLong])
1291         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1292     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1293         // implicit cast
1294         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1295     else if ([self WOTest_isUnsignedInt])
1296     {
1297         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1298         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned char)other); // explicit cast
1299     }
1300     else if ([self WOTest_isUnsignedShort])
1301         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1302     else if ([self WOTest_isUnsignedLong])
1303     {
1304         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1305         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned char)other); // explicit cast
1306     }
1307     else if ([self WOTest_isUnsignedLongLong])
1308     {
1309         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1310         // explicit cast
1311         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned char)other);
1312     }
1313     else if ([self WOTest_isFloat])
1314         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1315     else if ([self WOTest_isDouble])
1316          return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1317     else if ([self WOTest_isC99Bool])
1318         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1319
1320     // all other cases
1321     [NSException raise:NSInvalidArgumentException
1322                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1323
1324     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1325 }
1326
1327 - (NSComparisonResult)WOTest_compareWithInt:(int)other
1328 {
1329     if ([self WOTest_isChar]) // (also BOOL)
1330         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1331     else if ([self WOTest_isInt])
1332         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // no cast
1333     else if ([self WOTest_isShort])
1334         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1335     else if ([self WOTest_isLong])
1336         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1337     else if ([self WOTest_isLongLong])
1338         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1339     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1340                                     // implicit cast
1341         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1342     else if ([self WOTest_isUnsignedInt])
1343     {
1344         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1345         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned int)other); // explicit cast
1346     }
1347     else if ([self WOTest_isUnsignedShort])
1348         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1349     else if ([self WOTest_isUnsignedLong])
1350     {
1351         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1352         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned int)other); // explicit cast
1353     }
1354     else if ([self WOTest_isUnsignedLongLong])
1355     {
1356         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1357         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned int)other);  // explicit cast
1358     }
1359     else if ([self WOTest_isFloat])
1360         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1361     else if ([self WOTest_isDouble])
1362         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1363     else if ([self WOTest_isC99Bool])
1364         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1365
1366     // all other cases
1367     [NSException raise:NSInvalidArgumentException
1368                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1369
1370     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1371 }
1372
1373 - (NSComparisonResult)WOTest_compareWithShort:(short)other
1374 {
1375     if ([self WOTest_isChar]) // (also BOOL)
1376         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1377     else if ([self WOTest_isInt])
1378         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1379     else if ([self WOTest_isShort])
1380         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // no cast
1381     else if ([self WOTest_isLong])
1382         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1383     else if ([self WOTest_isLongLong])
1384         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1385     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1386                                     // implicit cast
1387         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1388     else if ([self WOTest_isUnsignedInt])
1389     {
1390         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1391         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned short)other); // explicit cast
1392     }
1393     else if ([self WOTest_isUnsignedShort])
1394         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1395     else if ([self WOTest_isUnsignedLong])
1396     {
1397         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1398         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned short)other); // explicit cast
1399     }
1400     else if ([self WOTest_isUnsignedLongLong])
1401     {
1402         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1403         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned short)other);  // explicit cast
1404     }
1405     else if ([self WOTest_isFloat])
1406         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1407     else if ([self WOTest_isDouble])
1408         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1409     else if ([self WOTest_isC99Bool])
1410         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1411
1412     // all other cases
1413     [NSException raise:NSInvalidArgumentException
1414                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1415
1416     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1417 }
1418
1419 - (NSComparisonResult)WOTest_compareWithLong:(long)other
1420 {
1421     if ([self WOTest_isChar]) // (also BOOL)
1422         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1423     else if ([self WOTest_isInt])
1424         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1425     else if ([self WOTest_isShort])
1426         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1427     else if ([self WOTest_isLong])
1428         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // no cast
1429     else if ([self WOTest_isLongLong])
1430         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1431     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1432                                     // implicit cast
1433         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
1434     else if ([self WOTest_isUnsignedInt])
1435     {
1436         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1437         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned long)other); // explicit cast
1438     }
1439     else if ([self WOTest_isUnsignedShort])
1440         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1441     else if ([self WOTest_isUnsignedLong])
1442     {
1443         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1444         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned long)other); // explicit cast
1445     }
1446     else if ([self WOTest_isUnsignedLongLong])
1447     {
1448         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1449         // explicit cast
1450         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned long)other);
1451     }
1452     else if ([self WOTest_isFloat])
1453         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1454     else if ([self WOTest_isDouble])
1455         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1456     else if ([self WOTest_isC99Bool])
1457         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1458
1459     // all other cases
1460     [NSException raise:NSInvalidArgumentException
1461                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1462
1463     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1464 }
1465
1466 - (NSComparisonResult)WOTest_compareWithLongLong:(long long)other
1467 {
1468     if ([self WOTest_isChar]) // (also BOOL)
1469         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1470     else if ([self WOTest_isInt])
1471         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1472     else if ([self WOTest_isShort])
1473         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1474     else if ([self WOTest_isLong])
1475         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1476     else if ([self WOTest_isLongLong])
1477         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // no cast
1478     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1479         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1480     else if ([self WOTest_isUnsignedInt])
1481         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1482     else if ([self WOTest_isUnsignedShort])
1483         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1484     else if ([self WOTest_isUnsignedLong])
1485         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1486     else if ([self WOTest_isUnsignedLongLong])
1487     {
1488         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1489         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned long long)other);  // explicit cast
1490     }
1491     else if ([self WOTest_isFloat])
1492         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1493     else if ([self WOTest_isDouble])
1494         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1495     else if ([self WOTest_isC99Bool])
1496         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1497
1498     // all other cases
1499     [NSException raise:NSInvalidArgumentException
1500                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1501
1502     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1503 }
1504
1505 - (NSComparisonResult)WOTest_compareWithUnsignedChar:(unsigned char)other
1506 {
1507     if ([self WOTest_isChar]) // (also BOOL)
1508         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1509     else if ([self WOTest_isInt])
1510         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1511     else if ([self WOTest_isShort])
1512         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1513     else if ([self WOTest_isLong])
1514         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1515     else if ([self WOTest_isLongLong])
1516         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1517     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1518         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // no cast
1519     else if ([self WOTest_isUnsignedInt])
1520         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1521     else if ([self WOTest_isUnsignedShort])
1522         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1523     else if ([self WOTest_isUnsignedLong])
1524         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1525     else if ([self WOTest_isUnsignedLongLong])
1526         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1527     else if ([self WOTest_isFloat])
1528         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1529     else if ([self WOTest_isDouble])
1530         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1531     else if ([self WOTest_isC99Bool])
1532         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1533
1534     // all other cases
1535     [NSException raise:NSInvalidArgumentException
1536                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1537
1538     return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
1539 }
1540
1541 - (NSComparisonResult)WOTest_compareWithUnsignedInt:(unsigned int)other
1542 {
1543     if ([self WOTest_isChar]) // (also BOOL)
1544     {
1545         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1546         return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // explicit cast
1547     }
1548     else if ([self WOTest_isInt])
1549     {
1550         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1551         return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
1552     }
1553     else if ([self WOTest_isShort])
1554     {
1555         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1556         return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
1557     }
1558     else if ([self WOTest_isLong])
1559     {
1560         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1561         return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
1562     }
1563     else if ([self WOTest_isLongLong])
1564         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1565     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1566         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1567     else if ([self WOTest_isUnsignedInt])
1568         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // no cast
1569     else if ([self WOTest_isUnsignedShort])
1570         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1571     else if ([self WOTest_isUnsignedLong])
1572         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1573     else if ([self WOTest_isUnsignedLongLong])
1574         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1575     else if ([self WOTest_isFloat])
1576         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1577     else if ([self WOTest_isDouble])
1578         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1579     else if ([self WOTest_isC99Bool])
1580         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1581
1582     // all other cases
1583     [NSException raise:NSInvalidArgumentException
1584                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1585
1586     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1587 }
1588
1589 - (NSComparisonResult)WOTest_compareWithUnsignedShort:(unsigned short)other
1590 {
1591     if ([self WOTest_isChar]) // (also BOOL)
1592         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1593     else if ([self WOTest_isInt])
1594         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1595     else if ([self WOTest_isShort])
1596         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1597     else if ([self WOTest_isLong])
1598         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1599     else if ([self WOTest_isLongLong])
1600         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1601     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1602         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1603     else if ([self WOTest_isUnsignedInt])
1604         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1605     else if ([self WOTest_isUnsignedShort])
1606         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // no cast
1607     else if ([self WOTest_isUnsignedLong])
1608         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1609     else if ([self WOTest_isUnsignedLongLong])
1610         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1611     else if ([self WOTest_isFloat])
1612         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1613     else if ([self WOTest_isDouble])
1614         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1615     else if ([self WOTest_isC99Bool])
1616         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1617
1618     // all other cases
1619     [NSException raise:NSInvalidArgumentException
1620                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1621
1622     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1623 }
1624
1625 - (NSComparisonResult)WOTest_compareWithUnsignedLong:(unsigned long)other
1626 {
1627     if ([self WOTest_isChar]) // char (also BOOL)
1628     {
1629         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1630         return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // expicit cast
1631     }
1632     else if ([self WOTest_isInt]) // int
1633     {
1634         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1635         return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
1636     }
1637     else if ([self WOTest_isShort]) // short
1638     {
1639         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1640         return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
1641     }
1642     else if ([self WOTest_isLong]) // long
1643     {
1644         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1645         return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
1646     }
1647     else if ([self WOTest_isLongLong]) // long long
1648         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1649     else if ([self WOTest_isUnsignedChar]) // unsigned char (also Boolean)
1650         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1651     else if ([self WOTest_isUnsignedInt]) // unsigned int
1652         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // implicit cast
1653     else if ([self WOTest_isUnsignedShort]) // unsigned short
1654         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1655     else if ([self WOTest_isUnsignedLong]) // unsigned long
1656         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // no cast
1657     else if ([self WOTest_isUnsignedLongLong]) // unsigned long long
1658         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1659     else if ([self WOTest_isFloat]) // float
1660         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1661     else if ([self WOTest_isDouble]) // double
1662         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1663     else if ([self WOTest_isC99Bool]) // C99 _Bool
1664         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1665
1666     // all other cases
1667     [NSException raise:NSInvalidArgumentException
1668                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1669
1670     return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
1671 }
1672
1673 - (NSComparisonResult)WOTest_compareWithUnsignedLongLong:(unsigned long long)other
1674 {
1675     if ([self WOTest_isChar]) // (also BOOL)
1676     {
1677         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1678         return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // explicit cast
1679     }
1680     else if ([self WOTest_isInt])
1681     {
1682         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1683         return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
1684     }
1685     else if ([self WOTest_isShort])
1686     {
1687         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1688         return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
1689     }
1690     else if ([self WOTest_isLong])
1691     {
1692         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1693         return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
1694     }
1695     else if ([self WOTest_isLongLong])
1696     {
1697         [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
1698         return WO_COMPARE_SCALARS((unsigned long long)[self WOTest_longLongValue], other); // explicit cast
1699     }
1700     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1701         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1702     else if ([self WOTest_isUnsignedInt])
1703         return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // implicit cast
1704     else if ([self WOTest_isUnsignedShort])
1705         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1706     else if ([self WOTest_isUnsignedLong])
1707         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1708     else if ([self WOTest_isUnsignedLongLong])
1709         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // no cast
1710     else if ([self WOTest_isFloat])
1711         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1712     else if ([self WOTest_isDouble])
1713         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1714     else if ([self WOTest_isC99Bool])
1715         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1716
1717     // all other cases
1718     [NSException raise:NSInvalidArgumentException
1719                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1720
1721     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1722 }
1723
1724 - (NSComparisonResult)WOTest_compareWithFloat:(float)other
1725 {
1726     if ([self WOTest_isChar]) // (also BOOL)
1727         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1728     else if ([self WOTest_isInt])
1729         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1730     else if ([self WOTest_isShort])
1731         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1732     else if ([self WOTest_isLong])
1733         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1734     else if ([self WOTest_isLongLong])
1735         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1736     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1737         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1738     else if ([self WOTest_isUnsignedInt])
1739         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1740     else if ([self WOTest_isUnsignedShort])
1741         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1742     else if ([self WOTest_isUnsignedLong])
1743         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1744     else if ([self WOTest_isUnsignedLongLong])
1745         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1746     else if ([self WOTest_isFloat])
1747         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // no cast
1748     else if ([self WOTest_isDouble])
1749         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1750     else if ([self WOTest_isC99Bool])
1751         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1752
1753     // all other cases
1754     [NSException raise:NSInvalidArgumentException
1755                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1756
1757     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1758 }
1759
1760 - (NSComparisonResult)WOTest_compareWithDouble:(double)other
1761 {
1762     if ([self WOTest_isChar]) // (also BOOL)
1763         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1764     else if ([self WOTest_isInt])
1765         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1766     else if ([self WOTest_isShort])
1767         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1768     else if ([self WOTest_isLong])
1769         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1770     else if ([self WOTest_isLongLong])
1771         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1772     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1773         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1774     else if ([self WOTest_isUnsignedInt])
1775         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1776     else if ([self WOTest_isUnsignedShort])
1777         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1778     else if ([self WOTest_isUnsignedLong])
1779         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1780     else if ([self WOTest_isUnsignedLongLong])
1781         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1782     else if ([self WOTest_isFloat])
1783         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1784     else if ([self WOTest_isDouble])
1785         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // no cast
1786     else if ([self WOTest_isC99Bool])
1787         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
1788
1789     // all other cases
1790     [NSException raise:NSInvalidArgumentException
1791                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1792
1793     return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
1794 }
1795
1796 - (NSComparisonResult)WOTest_compareWithC99Bool:(_Bool)other
1797 {
1798     if ([self WOTest_isChar]) // (also BOOL)
1799         return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
1800     else if ([self WOTest_isInt])
1801         return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
1802     else if ([self WOTest_isShort])
1803         return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
1804     else if ([self WOTest_isLong])
1805         return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
1806     else if ([self WOTest_isLongLong])
1807         return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
1808     else if ([self WOTest_isUnsignedChar]) // (also Boolean)
1809         return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
1810     else if ([self WOTest_isUnsignedInt])
1811         return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
1812     else if ([self WOTest_isUnsignedShort])
1813         return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
1814     else if ([self WOTest_isUnsignedLong])
1815         return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
1816     else if ([self WOTest_isUnsignedLongLong])
1817         return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
1818     else if ([self WOTest_isFloat])
1819         return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
1820     else if ([self WOTest_isDouble])
1821         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
1822     else if ([self WOTest_isC99Bool])
1823         return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // no cast
1824
1825     // all other cases
1826     [NSException raise:NSInvalidArgumentException
1827                 format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
1828
1829     return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
1830 }
1831
1832 @end