>*ANSI says that when you pass a va_list object to
a function and call va_arg() on it within that function, the value of
the va_list object is undefined upon return.
The relevant paragraph from the Standard is:
---- Begin Quote ----
The type declared is va_list
which is an object type suitable for holding information needed by the
macros
va_start, va_arg, va_end, and va_copy. If access to the varying
arguments is
desired, the called function shall declare an object (generally
referred to as ap in this
subclause) having type va_list. The object ap may be passed as an
argument to
another function; if that function invokes the va_arg macro with
parameter ap, the
value of ap in the calling function is indeterminate and shall be
passed to the va_end
macro prior to any further reference to ap.
---- End Quote ----
At first reading, I thought this was just saying the following:
* C passes arguments by value, i.e. the object in the calling
function remains unchanged
* The va_* macros alter their va_list argument
* You can only use the most up-to-date va_list, you can't use an
old out-of-date one
*Behold:
void parse_half_of_arg_list( va_list args )
{
* *...
* *foo_t x = va_arg( args, foo_t );
* *bar_t y = va_arg( args, bar_t );
* *...
}
void superfunc( int a, ... )
{
* *va_list args;
* *va_start( args, a );
* *parse_half_of_arg_list( args );
* */* Uh-oh! *ANSI says args is undefined now,
* * * but there's still more to parse */
* *parse_half_of_arg_list( args );
* *va_end( args );
}
The va_list object in "main" remains unchanged, therefore it is no
longer "up to date" when you go to try use another va_* macro on it.
So, I reasoned, why not pass a va_list * instead of a va_list? *Surely
that can't do any harm... I mean, since va_list is a data type, it
should be possible to create a pointer to it, and *(&x) should be the
same as x for any named type and for any operations, right? *Lo:
void parse_half_of_arg_list( va_list *args )
{
* *...
* *foo_t x = va_arg( *args, foo_t );
* *bar_t y = va_arg( *args, bar_t );
* *...
}
void superfunc( int a, ... )
{
* *va_list args;
* *va_start( args, a );
* *parse_half_of_arg_list( &args );
* *parse_half_of_arg_list( &args );
* *va_end( args );
}
I haven't been able to find anything in any standard that explicitly
allows or prohibits this, but there aren't many ways I can think of that
some devious library author could screw this up. *The thing is, this
code will exist for a long time and will quite possibly need to run
under every semi-major system and C compiler that will come into
existence in the next 20 years. *As of right now it will need to
immediately work on Sun, Mac, Cygwin, and Linux and compile under gcc,
pgcc, and icc.
I find the original paragraph from the Standard to be very strange. I
don't see why it went to the bother of mentioning calling and called
functions when it just could have said "the va_list must always be up-
to-date". This kind of leads me to believe that maybe there's some
other requirement than the va_list being up to date... maybe something
to do with the stack.
So, I guess the gist is that I want to do something really weird but I
want it to be really portable, and I'm afraid that might be an
impossibility. *Not being able to do this would mean I'd have to use a
bunch of structs or arrays with void pointers everywhere, which is a
considerable burden as this function will be called very frequently.
I am well aware that this is a really bizarre thing to have to do.
Please let me know if you know a system that breaks this, or if you have
good cause to believe that this will always work.
Thanks in advance.
Wait and see what others have to say.