That seems like a good idea. I tried the following for a runtime test
and it worked on several platforms (some array types, some not). With
casting to avoid compiler errors for the not-taken paths, it could
probably be used to detect the va_list type and use & or not. There's
probably some platform where va_list * and void * can't be compared
reliably (Cray?), making my test invalid though.
int VAListIsArray(int x, ...)
{
int isArray;
va_list args;
va_list * argsPtr;
va_start(args, x);
argsPtr = &args;
isArray = (((void *) argsPtr ) == ((void *) args));
va_end(args);
return(isArray);
}
I may just have to give up on the feature I'm trying to add or deal
with the maintenance issues of having a va_list * and va_list variants
of my underly code. Some of the platforms I have to deal with build
through custom IDE's provided by the vendor where it's not possible to
have it automatically run scripts to test for things like this.
Thanks for the help though.
Okay, I've had a chance to think about this a little longer now.
I suggest a solution as follows, using a .h/.c file pair (header
wrapping ifndef's not shown):
va_stuff.h:
#include <stdarg.h> /* just for convenience */
#define VA_LIST_IS_ARRAY_TYPE (0)
#if VA_LIST_IS_ARRAY_TYPE
# define VA_PARAMETER_LIST_ADDRESS(v) ((va_list*)(v))
#else
# define VA_PARAMETER_LIST_ADDRESS(v) (&(v))
#endif
va_stuff.c:
#include "va_stuff.h"
extern void
takes_a_va_list( va_list v ){
va_list *p = VA_PARAMETER_LIST_ADDRESS(v);
(void) &p;
}
(Probably this version of VA_PARAMETER_LIST_ADDRESS is exactly the same
as what I did before; I didn't check it against the previous one.)
Compile va_stuff.c with -Werror, or whatever the local equivalent is.
Any other file that needs to take the address of a va_list parameter
should #include "va_stuff.h" and use the macro.
Of course, the #define VA_LIST_IS_ARRAY_TYPE will give the wrong
value on platforms where va_list is an array type; but that's
okay, because the result will be a compilation error -- change the
0 to a 1, and everything is good to go. That also works the other
way -- if the #define indicates that va_list is an array type on a
platform where it isn't, again the result is a compilation error.
If va_stuff.c compiles without diagnostics, things are set okay,
and the macro for getting the address of a va_list parameter does
the right thing.
A downside of this approach is that using this macro removes some
type checking. The case where VA_LIST_IS_ARRAY_TYPE is 1 will
allow any pointer type, no questions asked. Fortunately, the most
likely common error (using a (va_list *) rather than a (va_list)
parameter) will do the right thing. Structure and union types
will give compilation errors, and correct type checking takes
place if VA_LIST_IS_ARRAY_TYPE is 0. So that isn't too bad. Plus
using the macro makes these all easy to find.
Of course, it would be nice if we could find a way to figure this
out in the program. And in fact it is possible to write a a small
number of functions that together determine whether va_list is an
array type or not, in a completely reliable and portable way.
(Exercise for the reader.) Unfortunately, that doesn't do any
good, because there's no way to take advantage of the information
in code that is accepted and works reliably on all platforms for
both the array case and the non-array case. So some sort of
preprocessor-based solution is necessary if it's important that
it be usable on any conforming implementation.