I want to implement a function Myprintf() that takes arguments like printf().
Now I am doing this by:
sprintf(demoString, "Num=%d String=%s", num, str);
Myprintf(demoString);
I want to replace this function call as:
Myprintf("Num=%d String=%s", num, str);
How is this possible?
#include <stdio.h>
#include <stdarg.h>
extern int Myprintf(const char *fmt, ...);
int Myprintf(const char *fmt, ...)
{
char buffer[4096];
va_list args;
va_start(args, fmt);
int rc = vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
...print the formatted buffer...
return rc;
}
It isn't clear from your question exactly how the output is done; your existing Myprintf() presumably outputs it somewhere, maybe with fprintf(). If that's the case, you might write instead:
int Myprintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int rc = vfprintf(debug_fp, fmt, args);
va_end(args);
return rc;
}
If you don't want to use the return value, declare the function as void and don't bother with the variable rc.
This is a fairly common pattern for 'printf() cover functions'.
You need to define a function with variable arguments, and use vsprintf to build the string.
The printf and its relatives are in a family of functions called variadic functions and there are macros/functions in the <stddef.h> header in the C standard library to manipulate variadic argument lists. Look at the GNU docs for examples: How Variadic Functions are Defined and Used.
Related
If the format string passed to vsprintf() (and variants thereof) contains no %-references, is it guaranteed that the va_list argument is not accessed?
Put another way, is:
#include <stdarg.h>
#include <stdio.h>
int main ( void ) {
char str[16];
va_list ap; /* never initialized */
(void)vsnprintf(str, sizeof(str), "", ap);
return 0;
}
a standard-conforming program? or is there undefined behavior there?
The example above is obviously silly, but imagine a function which can be called by both a variadic function and a fixed-args function, grossly simplified into something like:
void somefuncVA ( const char * fmt, va_list ap ) {
char str[16];
int n;
n = vsnprintf(str, sizeof(str), fmt, ap);
/* potentially do something with str */
}
void vfoo ( const char * fmt, ... ) {
va_list ap;
va_start(fmt, ap);
somefuncVA(fmt, ap);
}
void foo ( void ) {
va_list ap; /* no way to initialize this */
somefuncVA("", ap);
}
int vsprintf(char * restrict s, const char * restrict format, va_list arg);
If the format string passed to vsprintf() ... contains no %-references, is it guaranteed that the va_list argument is not accessed.
No.
The vsprintf function is equivalent to sprintf, with the variable argument list
replaced by arg, which shall have been initialized by the va_start macro .....
C11dr ยง7.21.6.13
Since the below code does not adhere to the spec, the result is undefined behavior (UB). No guarantees. #Eugene Sh.
va_list ap;
// vv-- ap not initialized
(void)vsnprintf(str, sizeof(str), "", ap);
Is vsprintf() guaranteed not to access va_list if format string makes no % references?
With a properly passed va_list arg, vsprintf() acts like sprintf(). Code like the following is OK. It is permissible to pass extra arguments. Via vsprintf(), they (the extra arguments) are not accessed, yet va_list arg may be accessed.
sprintf(buf, "format without percent", 1.2345, 456)`
If you don't have varargs passed to your function - your function isn't defined with ... as the last parameter - there's simply never any need for any use of va_list or va_start() in that function. If you want to pass an empty set of variable arguments, simply call the varargs function directly without any variable arguments - e.g., printf("\n");.
For example, instead of
void foo ( void ) {
va_list ap; /* no way to initialize this */
somefuncVA("", ap);
}
you can just write
void foo ( void ) {
vfoo("");
}
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.
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.
I want to define a function in C language which can take an unlimited number of arguments of any datatype.
For example: printf(), scanf(), etc.
Any idea on this?
To use variable number of arguments in C you need to include the header
#include <stdarg.h>
printf() in C is an example of function that takes variable number of arguments.
int printf(const char *fmt, ...)
More info here
Declare the function as taking a ... last argument. You'll need to use the macros from <stdarg.h> to access the arguments as a va_list.
If you just want something "like printf, but with a little extra behavior", then you can pass the va_list to vprintf, vfprintf, or vsprintf.
#include <stdarg.h>
#include <stdio.h>
#include <time.h>
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
void PrintErrorMsg(const char* fmt, ...)
{
time_t now;
char buffer[20];
va_list args;
va_start(args, fmt);
time(&now);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", gmtime(&now));
fprintf(stderr, "[%s] ", buffer);
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
va_end(args);
}
void printf(char* format, ...)
{
}
Look up Variadic Functions and varargs.h or stdarg.h (depending on the compiler).
See the <stdarg.h> header and related documentation.