In article <sl******************@poly.0lsen.net>
Clint Olsen <cl***@0lsen.net> writes:
I had this crazy idea to use stdarg macros to copy data in a generic
fashion, regardless of type. However, I'm not sure it will work in part
due to the ANSI C default argument promotions and the fact that va_arg
requires a type to [examine] ...
Not possible in general. Also, due to ANSI's wacky widening rules,
surprisingly hard to do even in specific, sometimes. :-)
So, essentially what I want in the variable argument function is
something that just assigns to a character type, and then copy a
predefined number of bytes to a target location (using memcpy or
whatever).
Example:
void foo(int numargs, size_t size, ...)
Usage:
/*
* Must use in type due to default argument promotions
*
foo(3, sizeof(int), 'b', 'a', 'r');
or:
/*
* Bar is some defined type and bar1..4 are variables of that type
*
foo(4, sizeof(Bar), bar1, bar2, bar3, bar4);
Any suggestions?
This fails for "typedef float Bar;" on a number of existing
implementations, which pass "int"s in integer registers and
floating-point values in floating-point registers. And of course,
"float"s are widened to "double"s, so just as you had to use
sizeof(int) in the first call, the sizeof(Bar) in the second
produces the wrong number (4 instead of 8).
Even if you fix the latter -- by defining, for instance, a name
for "promoted Bar" -- the former remains a problem. Suppose I
wish to call foo() with some values of type "long" on one of
these machines, which happens to have sizeof(long) == 8. (Note
that sizeof(double) is still 8.) Then:
foo(3, sizeof(long), 0L, 99L, 1L << 53);
passes three "long"s in integer registers -- actually, on at least
one of these machines, only two of them go in the integer registers
and the third is on a stack, but then I have to use "long long" to
make the example fly -- while:
foo(4, sizeof(Promoted_Bar), bar1, bar2, bar3, bar4);
passes the four "double"s in floating-point registers.
Inside foo(), all you have is the number 8. Were the parameters
passed via the integer registers, or in the FPU?
Note that the process gets particularly ugly for narrow types that
might or might not be unsigned and might or might not widen from
(say) 16 to 32 bits, or stay at 32 bits. For instance, suppose
you are using a POSIX header and the type "uid_t". Is uid_t
"unsigned short", or is it a plain short or an int or an unsigned
int? (The answer is different on different hosts; you will find
both results.) If it is unsigned short, and short is 16 bits and
int is 32 bits, it widens to signed int; but if it is unsigned int,
it stays unsigned int. If you needed portability to PDP-11s and
uid_t were unsigned short there, the 16-bit unsigned short would
widen to unsigned int.
(In practice, of course, unsigned and signed "int"s are always
passed the same way on these machines. The integer-vs-FP register
issue also only occurs if you allow floating-point parameters.
Thus, if you are willing to give up broad "pure" portability and
a certain amount of practical functionality, you can get away with
the trick you are aiming for. But at this point you might as well
not write it in C, or at least, not claim that the C is even remotely
portable -- just require a new implementation for each port, and
hope you never come across an un-handle-able situation like the
integer-vs-FP-registers one.)
--
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://67.40.109.61/torek/index.html (for the moment)
Reading email is like searching for food in the garbage, thanks to spammers.