U
Urs Thuermann
How can I pass a va_list to another function by reference? In a
simple libc implementation I have some functions as follows:
static int xprintf(struct callback *cb, const char *fmt, va_list ap)
{
...
switch (conv_spec) {
case 'u':
u = get_uint(conv_mods, &ap);
...
break;
case 'x':
u = get_uint(conv_mods, &ap);
...
case 'd':
n = get_int(conv_mods, &ap);
...
...
}
}
static unsigned long long get_uint(int mods, va_list *ap)
{
unsigned long long u;
switch (mods) {
case MOD_NONE:
case MOD_H:
case MOD_HH:
u = va_arg(*ap, unsigned int);
break;
case MOD_L:
u = va_arg(*ap, unsigned long int);
break;
case MOD_L:
u = va_arg(*ap, unsigned long long int);
break;
case MOD_Z:
u = va_arg(*ap, size_t);
break;
}
return u;
}
On my x86 32-bit system this compiles and works as intended. va_list
is simply a pointer. When I ported to x86_64 this produces compiler
warnings. On that system va_list is an array containing 1 element of
a struct type. Therefore, &ap has type struct __va_list_tag ** while
get_uint() expects an va_list* which is struct __va_list_tag (*)[1].
As a quick work-around I have put the va_list into a struct of which I
pass the address into get_uint(). This works on both systems and
seems to be portable but looks really ugly:
static int xprintf(struct callback *cb, const char *fmt, va_list ap)
{
va_struct aps;
va_copy(aps.ap, ap);
...;
switch (conv_spec) {
case 'u':
u = get_uint(conv_mods, &aps);
...;
}
va_end(aps.ap);
}
static unsigned long long get_uint(int mods, struct va_struct *aps)
{
...;
u = va_arg(aps->ap, unsigned int);
...;
}
Isn't there a cleaner way to portably pass a va_list to a function by
reference?
Also, what is the reason to typedef va_list as an array containing one
struct instead of just doing a typedef struct __va_list_tag va_list,
in which case no problem would occur with my original code? I think I
have read somewhere about this implementation as array but I don't
remember where and cannot find that document again.
urs
simple libc implementation I have some functions as follows:
static int xprintf(struct callback *cb, const char *fmt, va_list ap)
{
...
switch (conv_spec) {
case 'u':
u = get_uint(conv_mods, &ap);
...
break;
case 'x':
u = get_uint(conv_mods, &ap);
...
case 'd':
n = get_int(conv_mods, &ap);
...
...
}
}
static unsigned long long get_uint(int mods, va_list *ap)
{
unsigned long long u;
switch (mods) {
case MOD_NONE:
case MOD_H:
case MOD_HH:
u = va_arg(*ap, unsigned int);
break;
case MOD_L:
u = va_arg(*ap, unsigned long int);
break;
case MOD_L:
u = va_arg(*ap, unsigned long long int);
break;
case MOD_Z:
u = va_arg(*ap, size_t);
break;
}
return u;
}
On my x86 32-bit system this compiles and works as intended. va_list
is simply a pointer. When I ported to x86_64 this produces compiler
warnings. On that system va_list is an array containing 1 element of
a struct type. Therefore, &ap has type struct __va_list_tag ** while
get_uint() expects an va_list* which is struct __va_list_tag (*)[1].
As a quick work-around I have put the va_list into a struct of which I
pass the address into get_uint(). This works on both systems and
seems to be portable but looks really ugly:
static int xprintf(struct callback *cb, const char *fmt, va_list ap)
{
va_struct aps;
va_copy(aps.ap, ap);
...;
switch (conv_spec) {
case 'u':
u = get_uint(conv_mods, &aps);
...;
}
va_end(aps.ap);
}
static unsigned long long get_uint(int mods, struct va_struct *aps)
{
...;
u = va_arg(aps->ap, unsigned int);
...;
}
Isn't there a cleaner way to portably pass a va_list to a function by
reference?
Also, what is the reason to typedef va_list as an array containing one
struct instead of just doing a typedef struct __va_list_tag va_list,
in which case no problem would occur with my original code? I think I
have read somewhere about this implementation as array but I don't
remember where and cannot find that document again.
urs