tags:

views:

810

answers:

1

I'm writing a function in C that takes a variable number of arguments.

size_t myprintf(char *fmt, ...);

So far, so good. I've decided it's best to do things the Right Way™ and make a version that takes variable arguments, and another version that takes a va_list.

size_t myprintf(char *fmt, ...);
size_t myvprintf(char *fmt, va_list args);

Not that hard to do. Except my_vprintf() needs to send its args out to two different functions (first to snprintf() with a length of 0 to determine how much room we need, then to sprintf() after we've allocated that much room). I do this with va_copy.

size_t myvprintf(char *fmt, va_list args)
{
    va_list args2;
    va_copy(args, args2);
    // do stuff with args2
    va_end(args2);
    // do more stuff with args
}

This is all fine and dandy, but C99 is a bit poorly implemented. I would like, if possible, for my code to work in C89 as well, and to work with as many compilers and on as many platforms as possible. I currently have this after #include <stddef.h> but before any code:

#ifndef va_copy
# ifdef __va_copy
#  define va_copy(a,b) __va_copy(a,b)
# else /* !__va_copy */
#  define va_copy(a,b) ((a)=(b))
# endif /* __va_copy */
#endif /* va_copy */

I am led to believe that ((a)=(b)) is unreliable, and that I should perhaps use memcpy() or something similar, but this is still on the level of "If you don't support C99, I hope it works" rather than "If you don't support C99, never fear" (which is what I want). Is there any good way to get around this limitation? I've seen a few solutions - va_list functions that eat one argument and recurse, passing the va_list twice so that two separate copies are made, etc. - but I don't know how well they would work (and the recursive solution won't do so well if I just want to call vsnprintf(), now, will it?).

So I turn to you, StackOverflow User. Is there anything more I can do to provide C89 compatibility, or are users without va_copy and __va_copy (admittedly few and far between) just going to have to suck it in and take it?

+1  A: 

(a)=(b) is unreliable. Even passing two va_list is (the public function would be a simple wrapper) as va_list can be something like:

typedef __va_list_impl va_list[1];

for which doing a va_arg on one will be modify the other (I seem to remember that Solaris use such kind of thing, ah, the register windows...). Sadly, I know of no sure way to do what you want.

AProgrammer
I was afraid of this. Oh, well. I suppose the appropriate thing to do would be to change my hack-`va_copy` to `#error "Get a new compiler"`
Chris Lutz
In this particular case the assignment in the va_copy macro would fail, since you can't assign one array to another.
caf