I have a function that tries to log stuff to the console and also to a log file, but it doesn't work. The second use of the variable length argument gives garbage written to the console. Any ideas?
void logPrintf(const char *fmt, ...) {
va_list ap; // log to logfile
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_list ap2; // log to console
va_start(ap2, fmt);
printf(fmt, ap2);
va_end(ap2);
}
It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter. This enables access to variadic function arguments. *argN* is the last fixed argument in the variadic function.
Variadic parameters (Variable Length argument) are Python's solution to that problem. A Variadic Parameter accepts arbitrary arguments and collects them into a data structure without raising an error for unmatched parameters numbers.
A function with a parameter that is preceded with a set of ellipses ( ... ) is considered a variadic function. The ellipsis means that the parameter provided can be zero, one, or more values.
In Swift, variadic parameters are the special type of parameters available in the function. It is used to accept zero or more values of the same type in the function. It is also used when the input value of the parameter is varied at the time when the function is called.
The original code fails because it tries to use printf()
where it needs to use vprintf()
. Taking dubious points like the logOpen
and logClose
statements at face value (given the notation, presumably they're macros which open and close the flog
file stream), the code should be:
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_list ap2;
va_start(ap2, fmt);
vprintf(fmt, ap2);
va_end(ap2);
}
There's no particular requirement to use two separate va_list
variables; it is perfectly OK to use the same one twice as long as you use va_end()
before you use va_start()
again.
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
When a va_list
value is passed to another function (vfprintf()
and vprintf()
in this code), you should assume that it is no longer usable in the current function. It is only safe to call va_end()
on it.
There is no need for va_copy()
in this code. It works, but it isn't needed. You need va_copy()
in other circumstances, such as when your function is passed a va_list
and you need to process the list twice:
void logVprintf(const char *fmt, va_list args1)
{
va_list args2;
va_copy(args2, args1);
logOpen;
vfprintf(flog, fmt, args1);
logClose;
vprintf(fmt, args2);
va_end(args2);
}
Note that in this code, it is the calling code's responsibility to call va_end()
on args1
. Indeed, the standard says:
Each invocation of the
va_start
andva_copy
macros shall be matched by a corresponding invocation of theva_end
macro in the same function.
Since the logVprintf()
function doesn't call either va_start
or va_copy
to initialize args1
, it cannot legitimately call va_end
on args1
. On the other hand, the standard requires it to call va_end
for args2
.
The logPrintf()
function can be implemented in terms of logVprintf()
now:
void logPrintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
logVprintf(fmt, args);
va_end(args);
}
This structure — an operational function that takes a va_list
and a cover function that takes ellipsis (variable arguments) and passes them to the operational function after conversion to a va_list
— is often a good way to work. Sooner or later, you usually find a need for the version with a va_list
argument.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With