(I am not sure why no one ever replied to this earlier, but I have
been saving it and have finally gotten around to replying myself.)
In article <1177727222.841287.113000@o5g2000hsb.googlegroups. com>
pete m <pmac360@yahoo.comwrote:
Quote:
>I would like to support stdio functions for an extremely primitive
>type system, as shown in the attached sample program, using dynamic
>creation of a va_list.
This cannot be done portably.
(It *can* be done *non*-portably, once for each target machine.
In some cases the technique may require compiler support and/or
sneaky assembly code. To get the "right" sizes, rather than using
a large array and simply hoping it is big enough -- as you did in
some code I snipped below -- may require a two-pass approach.)
[much snippage -- retaining only the va_assign related code]
Quote:
>#define va_start_assign(list, var) ((list) = ((char*)&var))
>
>#define va_assign(lvalp, type, rval) \
*((type*)lvalp) = rval; \
va_arg(lvalp, type);
>
>#define VA_FLOAT double
>#define VA_CHAR int
...
Quote:
va_list out;
double aligned[100]; /* aligned buffer of ridiculously large size */
va_start_assign(out, aligned[0]);
for(i = 0; arg[i].t != T_END; i++) {
switch(arg[i].t) {
case T_INT:
va_assign(out, int, arg[i].i);
break;
case T_FLOAT:
va_assign(out, VA_FLOAT, arg[i].f);
break;
case T_CHAR:
va_assign(out, VA_CHAR, arg[i].c);
break;
case T_STRING:
va_assign(out, const char *, arg[i].s);
break;
default:
assert(0);
va_end(out);
return -1;
}
}
>
va_end(out);
va_start_assign(out, aligned[0]);
("out" is then used as if it were an ordinary "va_list" parameter
created by va_start().)
This is likely to work on a typical stack-based system, such as
normally used on x86 implementations. It may even work on some
systems that pass arguments in registers. It will, however, fail
on some other systems that pass arguments in registers. For
instance, on V9 SPARC, "out" needs to point to a structure
object, where the structure contains two counters and three
pointers. The two counters indicate how many integral and
floating-point parameters remain unprocessed in the "save areas"
to which the first two pointers point. The final pointer points
to the "overflow area" for any additional parameters.
Hence, to "va_assign" (as it were) a VA_FLOAT, you would (at least
nominally) want to copy the "double" to the floating-point save
area and increment the floating-point count. In practice, you
could "cheat" and simply set the two counters to zero, then dump
all the remaining arguments into the "pseudo-stack" as you do above:
#define va_start_assign(ptr, save_area) \
(((struct __va_args *)&(save_area))->__va_ni = 0, \
((struct __va_args *)&(save_area))->__va_nf = 0, \
((struct __va_args *)&(save_area))->__va_rest = \
(void *)((struct va_args *)&(save_area) + 1), \
(ptr) = (struct __va_args *)&(save_area))
This method is of course quite specific to the SPARC-V9 ABI (and
makes some assumptions about the compiler's internal names for
various pieces involved in <stdarg.h>; and I may not recall them
quite correctly, and/or they may depend on specific compiler
revisions).
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.