On 12/16/2010 11:32 PM, Ered China Luin wrote:
> [...]
> You can pass any type through a var-args call, but get the arguments with va_arg
> and don't lie about the type.
Note that va_arg() must specify the *promoted* type: `double' and
`int' instead of `float' and `short', for example. This makes trouble
with types whose promotion rules differ from one system to another.
For example, in
enum TriplePlay { TINKER, EVERS, CHANCE } infielder = CHANCE;
void func(const char *, ...);
func ("What is the type of the second parameter?", infielder);
The type of the second *argument* is `enum TriplePlay', which we know
is "compatible with char, a signed integer type, or an unsigned integer
type," at the implementation's discretion. The compatible type the
implementation chooses may (or may not be) subject to promotion, and if
promotable may promote to pretty much anything. So, what type should
the va_arg() macro use to retrieve the second parameter? Different
implementations will give different answers (and `enum TriplePlay' will
not be among them, if that is a promotable type).
Even `size_t' and `ptrdiff_t' are theoretically subject to this
problem, although in practice they are usually wide enough that they
do not promote. Still, it is possible for a Standard-conforming C to
have `SIZE_MAX < INT_MAX', and if this happens a `size_t' argument
will promote to an `int' parameter and `va_arg(ap, size_t)' will be
incorrect.
For ultimate safety in passing (possibly) promotable types to
varargs functions, I guess you could wrap the suspect value in a
struct or union, types that are never promoted:
union Wrapper { enum TriplePlay payload; } carrier;
carrier.payload = infielder;
func ("The second parameter is a `union Wrapper'.", carrier);
Another possibility is to pass a pointer:
func ("The second parameter points to an `enum TriplePlay'.",
&infielder);
I confess, though, that I've never seen these dodges used -- not even
by people who've previously fallen afoul of an unexpected promotion!
--
Eric Sosman
lid