I'm trying to pass __ VA_ARGS __ to a function. For some reason the first argument is always wrong (seems like an address):
#define PRINTF_ERROR(format, ...) {\
PrintfError(format, __FUNCTION__, ##__VA_ARGS__);\
}
void PrintfError(const char* format, const char* function, ...)
{
va_list args;
va_start(args, format);
printf("%s(): ", function);
printf(format, args);
va_end(args);
}
For example, when trying to print the same variable:
"A = 0x20005524 A = 0x00000007"
Does anybody know why?
Thanks
There are two problem here.
First, va_start expects the last named parameter of the current function as its second parameter. In this case that would be function.
The second issue is that you're passing a va_list into printf. You should be calling vprintf instead.
void PrintfError(const char* format, const char* function, ...)
{
va_list args;
va_start(args, function); // pass "function" instead of "format"
printf("%s(): ", function);
vprintf(format, args); // call vprintf
va_end(args);
}
You've got the order of your parameters wrong. The one you pass to va_start() has to be the one before the ... because it's used to work out where the extra arguments start
So your function should look like this...
void PrintfError(const char* function, const char* format, ...)
{
va_list args;
va_start(args, format);
From man va_start:
void va_start(va_list ap, last);
[...]
DESCRIPTION
va_start()
[...]
The argument last is the name of the last argument before the variable argument list, that is, the last argument of which the calling function knows the type.
So given
void PrintfError(const char* format, const char* function, ...)
just change
va_start(args, format);
to be
va_start(args, function);
Also this
printf(format, args);
(which probably is a typo) should be
vprintf(format, args);
Unrelated to your question, here
#define PRINTF_ERROR(format, ...) {\
PrintfError(format, __FUNCTION__, ##__VA_ARGS__);\
}
the curly braces are only half the way to make this safe.
Better do
#define PRINTF_ERROR(format, ...) do {\
PrintfError(format, __FUNCTION__, __VA_ARGS__);\
} while (0)
Also no need for the ##.
Related
Can I use va_list as following:
void myself_printf(char* key, char* value, ...) {
char real_key[1024];
char real_value[1024];
va_list args;
va_start(args, value);
// use args twice format key and value
vsnprintf(real_key, 1024000, key, args);
vsnprintf(real_value, 1024000, value, args);
va_end(args);
}
using in demo
myself_printf("%d-%s", "%s-%d", 12, "key", "value", 24);
expect: real_key is "12-key", and real_value is "value-24"
Strictly speaking, you must use va_copy, since vsnprintf can invalidate args.
void myself_printf(char* key, char* value, ...) {
char real_key[1024000];
char real_value[1024000];
va_list args, args2;
va_start(args, value);
va_copy(args2, args);
// use args twice format key and value
vsnprintf(real_key, 1024000, key, args);
vsnprintf(real_value, 1024000, value, args2);
va_end(args);
va_end(args2);
}
This is what va_copy is designed for.
As noted, this is a large amount of stack space, although it is within typical stack sizes. Consider using vasprintf, if it is available.
Citations
n1548 §7.16
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…
In other words, you cannot portably use args after you pass it to vsnprintf.
This is clarified in footnote 281:
As the functions vfprintf, vfscanf, vprintf, vscanf, vsnprintf, vsprintf, and vsscanf invoke the va_arg macro, the value of arg after the return is indeterminate.
While it seems that va_list is passed by value, that does not mean that it is actually passed by value, or that the va_list itself encapsulates all of its state. A common trick with typedefs in C is to declare them as a 1-element array:
typedef int my_type[1];
Since my_type decays to pointer type when it is passed by function, it only appears to be passed by value.
Demo
#include <stdio.h>
#include <stdarg.h>
void func(const char *msg, ...) {
va_list ap;
va_start(ap, msg);
vfprintf(stdout, msg, ap);
vfprintf(stdout, msg, ap);
va_end(ap);
}
int main(int argc, char **argv) {
func("%d + %d = %d\n", 2, 3, 5);
return 0;
}
On my computer, the output is:
2 + 3 = 5
590862432 + -1635853408 = 1586038440
As an alternative to using va_copy() — as suggested by Dietrich Epp in an answer — you can simply use va_start() and va_end() twice.
void myself_printf(char *key_fmt, char *value_fmt, ...)
{
char real_key[1024];
char real_value[1024];
va_list args;
va_start(args, value);
vsnprintf(real_key, sizeof(real_key), key_fmt, args);
va_end(args);
vs_start(args, value);
vsnprintf(real_value, sizeof(real_key), value_fmt, args);
va_end(args);
…do something useful with real_key and real_value…
}
The original version of the question used char real_key[1024000]; and similarly for real_value. Allocating almost 2 MiB of data on the stack won't work reliably on Windows (the limit there is usually 1 MiB of stack), and uses an awful lot of space on Unix systems (where the stack size is usually 8 MiB). Be cautious!
You would need to use va_copy() if the va_list was passed as an argument to your function. For example:
void myself_printf(char* key, char* value, ...)
{
va_list args;
va_start(args, value);
myself_vprintf(key, value, args);
va_end(args);
}
void myself_vprintf(char *key_fmt, char *value_fmt, va_list args1)
{
char real_key[1024];
char real_value[1024];
va_list args2;
va_copy(args2, args1);
vsnprintf(real_key, sizeof(real_key), key_fmt, args1);
vsnprintf(real_value, sizeof(real_value), value_fmt, args2);
va_end(args2);
…do something useful with real_key and real_value…
}
The specification of va_end() says:
The va_end macro facilitates a normal return from the function whose variable argument list was referred to by the expansion of the va_start macro, or the function containing the expansion of the va_copy macro, that initialized the va_list ap. The va_end macro may modify ap so that it is no longer usable (without being reinitialized by the va_start or va_copy macro). If there is no corresponding invocation of the va_start or va_copy macro, or if the va_end macro is not invoked before the return, the behavior is undefined.
Note that the specification of the vfprintf() function includes footnote 288 which says:
As the functions vfprintf, vfscanf, vprintf, vscanf, vsnprintf, vsprintf, and vsscanf invoke the va_arg macro, the value of arg after the return is indeterminate.
Granted, footnotes are not normative, but this is a strong indication that the functions are expected to use va_arg and hence the double use of arg as shown in the question leads to undefined behaviour unless there is an intervening call to va_end and another va_start or a use of va_copy (and its matching va_end).
This question already has answers here:
How to pass variable number of arguments to printf/sprintf
(7 answers)
Closed 7 years ago.
i want to write a function in c which uses the elipsis (...) argument, but i have no idea how it works.
i want to do something like this:
void error(const char* fmt, ...);
void error(const char* fmt, ...) {
// fprintf(stderr, fmt, ...); << didnt work!
fprintf(stderr, fmt, /* ??? */);
}
i want to use it like a "normal" printf() call.
error("bla");
error("nr: %d", 42);
error("pi: %f", 3.1415);
how can i access the elipsis as hole thing and pass it to the next function?
The ellipsis does not constitute a "pack" in any way that you can handle or forward directly. The only way you can manage function arguments that don't match any function parameters is via the <stdarg.h> features.
This means that for every variable function foo you should also always have a corresponding function vfoo that consumes a va_list. For example:
#include <stdarg.h>
void foo(const char * fmt, ...);
void vfoo(const char * va_list ap);
The former is typically implemented in terms of the latter:
void foo(const char * fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfoo(fmt, ap);
va_end(ap);
}
Luckily, printf family of functions follows these rules. So when you want to farm out to a printf function, you actually use the corresponding underlying vprintf version:
void error(const char * fmt, ...)
{
do_stuff();
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
Of course following the same rules you should write a verror function first and make that one call vprintf:
void verror(const har * fmt, va_list ap)
{
do_stuff();
vfprintf(stderr, fmt, ap);
}
If I understand your question properly, what you're looking for is information on variable arguments or a Variadic Function. Under C you will want to research the 'stdargs.h' header and associated manual page. Here is a simple example that takes an arbitrary number of integers and returns the average.
#include <stdarg.h>
float average(int v, ...)
{
va_list args;
int i = 0;
int num = 0;
va_start(args, v);
for (; v; v--){
i += va_arg(args, int);
num++;
}
va_end(args);
return (float)i/num;
}
See stdarg.h for dealing with functions with variable arguments.
For the specific case of simply passing the variable number of arguments to fprintf, there is vfprintf for that, e.g.,
void error (const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
For such a simple case you may also consider variadic macros (introduced in C99), e.g.,
#define error(fmt, ...) fprintf(stderr, "Error: " fmt, __VA_ARGS__)
This question already has answers here:
Passing variable arguments to another function that accepts a variable argument list
(11 answers)
Closed 8 years ago.
I have a function log_message it takes variable arguments.
log_message(int level, char *fmt, ...)
now before calling this(log_message) function i have to add new function(_log_message), and new function will call log_message.
_log_message(int level, char *fmt, ...)
new function is also same. when _log_message will call log_message it will convert variable input to va_list. Now i have va_list, i don't wanna change the original one, is there any way to change back to variable input, so i will able to call the original one(log_message).
No, there is no way to turn a va_list back into a list of arguments.
The usual approach is to define a base function which takes a va_list as an argument. For example, the standard C library defines printf and vprintf; the first is a varargs function and the second has exactly the same functionality but takes a va_list instead. Similarly, it defines fprintf and vfprintf. It's trivial to define printf, vprintf and fprintf in terms of vfprintf:
int fprintf(FILE* stream, const char* format, ...) {
va_list ap;
va_start(ap, format);
int n = vfprintf(stream, format, ap);
va_end(ap);
return n;
}
int vprintf(const char* format, va_list ap) {
return vfprintf(stdout, format, ap);
}
int printf(const char* format, ...) {
va_list ap;
va_start(ap, format);
int n = vprintf(format, ap);
va_end(ap);
return n;
}
(Similarly for the various exec* functions, which come in both va_list and varargs varieties.)
I'd suggest you adopt a similar strategy.
People say use vprintf instead of printf. But I can't because I need to use a function of my own.
I have 2 custom functions
both use va_list
one function uses the other function
So is it possible to chain the va_list calls without having those v... functions ? And how ?
Code:
char* createString(const char* line, ...)
{
char* result = (char*)malloc(100);
va_list args;
va_start(args, line);
vsprintf(result, line, args);
va_end(args);
return result;
}
void show(const char* line, ...)
{
va_list args;
va_start(args, line);
char* a = createString(line, args);
va_end(args);
AfxMessageBox(a);
free(a);
}
// usage:
show("test %i, %i", 12, 123);
When I try this I get wrong strings displayed. Instead of 12 and 123 I get some pointers or stuff.
Sad solution:
char* vCreateString(const char* line, va_list args)
{
char* result = (char*)malloc(100);
vsprintf(result, line, args);
return result;
}
No, it's not possible. That's why the v-versions exist in the first place, to support passing on a va_list.
You need to make createString() accept a va_list argument.
Also, please don't cast the return value of malloc() in C, and consider using vsnprintf() to protect against buffer overflow.
I'm trying to wrap printf in a C program (well, actually _snprintf but this example is simpler) and am having trouble getting the variable argument stuff to work. Here is my code:
#include <stdio.h>
#include <stdarg.h>
void works(void)
{
printf("%d\n", 100);
}
void wrap_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
printf(fmt, args);
va_end(args);
}
void broken(void)
{
wrap_printf("%d\n", 100);
}
int main(void)
{
works();
broken();
return 0;
}
Here is my output:
100
3668388
The args variable looks good after the call to va_start in my code, but as soon as I step into the C runtime code and they call va_start the value looks bad. Any thoughts as to what I might be doing wrong?
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
You need to call vprintf instead of printf. The v*printf functions understand va_List arguments. I'm surprised you didn't get a warning.
You're passing args which is a va_list, but printf() of course expects the arguments directly, it has no way of knowing that its second argument suddenly is a va_list.
You should be using vprintf(), the variable-argument version which does indeed expect a va_list and knows how to extract the values from it.