va_list is not va_list
This is somewhat mysterious bug I ran into. I call it a bug, though what it does is possibly sensible. Anyway, just from looking at it you will never figure it out. :)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#import <Foundation/Foundation.h>
//
// just a small struct to carry parser context around
// pared down to va_list. (You need to use va_list *)
//
typedef struct
{
va_list *args;
} parse_context;
//
// this functions sets the parse_context up
//
static void init_parse_context( parse_context *p,
va_list *args)
{
p->args = args;
}
//
// actual "parse" method
//
static void _parse( parse_context *p)
{
NSString *s;
s = va_arg( *p->args, NSString *);
NSLog( @"%@", s);
}
//
// convenience method (which crashes)
//
static void parse( va_list args)
{
parse_context context;
/// ***
/// *** WARNING see below ***
init_parse_context( &context, &args);
/// ***
/// ***
_parse( &context);
}
@interface Foo : NSObject
+ (void) test:(NSString *) format, ...;
@end
@implementation Foo
+ (void) test:(NSString *) format, ...
{
va_list args;
parse_context context;
// this is OK
va_start( args, format);
init_parse_context( &context, &args);
_parse( &context);
va_end( args);
// this is not
va_start( args, format);
parse( args);
va_end( args);
}
@end
You probably need the help of the compiler. It produces the following warning - rightfully so, the resultant program crashes:
warning: incompatible pointer types passing '__va_list_tag **' (aka 'struct __va_list_tag **') to parameter of type 'va_list *' (aka '__builtin_va_list *') [-Wincompatible-pointer-types]
init_parse_context( &context, &args);
^~~~~
more.m:19:43: note: passing argument to parameter 'args' here
va_list *args)
^
1 warning generated.
Now why is this weird ? Obviously parse()
just does, what is done in +test
directly namely:
What is interesting is is, that a “passed” va_list isn’t of type va_list anymore but instead of type struct __va_list_tag *
. Weird and unexpected and in terms of my mental model of C-types wrong. But maybe necessary in terms of stdarg implementation ?
Post a comment
All comments are held for moderation; basic HTML formatting accepted.