I have zillions of my_printf() function calls in a huge program. I now want to convert them all so that the function takes a new integer argument (call it x) without having to edit the zillions of calls. If my_printf only ever took exactly one string argument then I could do something like this:
#define my_printf(str) _my_printf(x,str)
void _my_printf(int x,char *str) // changed from my_printf(char *str)
{
// stuff
}
But as my_printf takes a variable number of arguments I'm not sure how to do it. Can it be done?
EDIT: for those wondering why I should want to do such a thing, here's a related example:
#if BELT_AND_BRACES_DIAGNOSTIC_MODE
#define function(x) _function(__FILE__,__LINE__,x)
#else // speed critical optimised mode
#define function(x) _function(x)
#endif
#if BELT_AND_BRACES_DIAGNOSTIC_MODE
void _function(char *file,int line,int x)
#else
void _function(int x)
#endif
{
// stuff
#if BELT_AND_BRACES_DIAGNOSTIC_MODE
if (something_went_wrong)
{
printf("Cock up in function when called from %s line %d\n",file,line);
}
#endif
}
You may use C99 variadic macros:
#define my_printf(...) my_printf_(x, __VA_ARGS__)
As Microsoft's implementation suppresse trailing commas, the str argument can be added explicitly
#define my_printf(str, ...) my_printf_(x, str, __VA_ARGS__)
but this would lead to a syntax error in standard C when invoked without variadic arguments
my_printf("foo")
or an empty argument list
my_printf("foo",)
Therefore, I'd go with the first version.
If the code can be compiled as C99 code, you can define a variadic macro
#define my_printf(str, args...) _my_printf(x, str, ##__VA_ARGS__)
The preprocessor will replace the arguments ... and the GNU preprocessor will remove the trailing comma in case the macro is invoked only with the str argument.
The best thing is of course to bite the bullet and edit the code. Otherwise you're creating a "mystery", that needs to be solved by all future maintainers of the code. Even if that's only you, this is exactly the kind of clever trick that you will forget all about. It sucks to come back, and be puzzled by strange pointless-seeming macros.
That said, if you're using a GNU toolchain, you can perhaps look into using varargs macros.
Not with standard C89 macros, you can't. However you can get the same effect using functions, by breaking out the main part of your my_printf function into a vmy_printf function, analagous to the standard vprintf:
#include <stdarg.h>
int vmy_printf(int x, const char *format, va_list ap)
{
/* main body of my_printf goes here, taking its varargs from ap */
}
/* new_my_printf(), for callers who know about the x parameter */
int new_my_printf(int x, const char *format, ...)
{
int n;
va_list ap;
va_start(ap, format);
n = vmy_printf(x, format, ap);
va_end(ap);
return n;
}
/* my_printf(), for the old callers who don't know about x */
int my_printf(const char *format, ...)
{
int n;
va_list ap;
va_start(ap, format);
n = vmy_printf(DEFAULT_X, format, ap);
va_end(ap);
return n;
}
(This kind of thing is why those v... versions of all the standard varargs functions exist.)
If my_printf already takes a variable number of arguments, I'm not sure why you need to wrap 'one more argument' in a macro... Just insert the new calls with the extra argument and be done with it; the old calls should still work as expected.
A simple solution to this problem is...
#define my_printf(x) printf x
(note the missing braces)
To call it, use:
my_printf((any number of arguments))
(note the double braces)
Related
I see this link Passing variable arguments to another function that accepts a variable argument list. What is the syntax to pass it to a macro as well?
#include <stdarg.h>
#define exampleW(int b, args...) function2(b, va_args)
static void exampleV(int b, va_list args);
void exampleB(int b, ...)
{
va_list args;
va_start(args, b);
exampleV(b, args);
//also pass args to a macro which takes multiple args after this step
??? [Is it exampleW(b, ##args)]
va_end(args);
}
static void exampleV(int b, va_list args)
{
...whatever you planned to have exampleB do...
...except it calls neither va_start nor va_end...
}
This is not possible. Macros are expanded at compile time, and so their arguments need to be known at compile time, at the point where the macro is used. The arguments to your function exampleB are in general not known until run time. And even though in many cases the arguments may be known when the call to the function is compiled, that may be in a different source file, which does not help you with macros in this source file.
You'll need to either:
have exampleB instead call a function like vfunction2 which is function2 rewritten to take a va_list parameter
reimplement exampleB as a macro
if there are a finite number of possible combinations of arguments to exampleB, then write code to handle all the cases separately:
if (b == TWO_INTS) {
int i1 = va_arg(args, int);
int i2 = va_arg(args, int);
exampleW(i1, i2);
} else if (b == STRING_AND_DOUBLE) {
char *s = va_arg(args, char *);
double d = va_arg(args, double);
exampleW(s,d);
} else // ...
do something non-portable to call the function function2 with the same arguments as were passed to exampleB, e.g. with assembly language tricks, or gcc's __builtin_apply().
I have a function which implements a custom variant of printf.
Does anyone have a clue how I can print out one more parameter with va_list? My format is like "%d some text %s". I need a, to be printed as first parameter.
void func (int a, char *fmt, ...) {
va_list ap;
va_start (ap, fmt);
// vprintf(stdout, fmt, a, ap) //Can't do like this :(
vfprintf(fmt, a, ap); //Or like this :(
va_end(ap);
}
If a is always printed the same way, it shouldn't be part of the format passed in. You should instead print it separately.
In the example you gave, change your format string to "some text %s". Then your function can do this:
void func (int a, char *fmt, ...) {
va_list ap;
// first print the function specific fields
printf("%d ", a);
// then the user's format
va_start (ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
As noted in comments:
Redesign the interface
You would do best to redesign the interface so that the format string only applies to the ... (va_list) arguments; the fixed argument gets treated separately.
Standard reasons for wanting extra arguments are logging things like __FILE__ and __LINE__ (presumably not __func__ if you don't have C99 __VA_ARGS__ support — see below). You would probably do best to remove the option of caller-controlled formatting of those fixed arguments, leaving the fmt to process just the ... arguments.
If you can't redesign the interface
The fact that the %d is part of the format and the int value is not part of the va_list makes life tricky.
If you cannot, or decline to, change the rules for calling the function, you'll need to parse the format, for example separating it into the part up to (but not including) the second %, and use that format with the standalone argument; the remainder of the format is then passed to vprintf(). If you're on a POSIX system and in a multi-threaded program, you might want to use flockfile() and funlockfile() on stdout to ensure that they're treated as a single unit of output despite the multi-threading.
// Using C99 features
void func(int a, char *fmt, ...)
{
char *p1 = strchr(fmt, '%');
if (p1 == 0)
{
// No conversion specifications. Not puts(); it adds a newline
fputs(fmt, stdout);
return;
}
char *p2 = strchr(p1 + 1, '%');
if (p2 == 0)
{
// The user invoked func(1, "iteration %d:\n");?
printf(fmt, a);
return;
}
int buflen = p2 - fmt + 1;
char buffer[buflen];
memmove(buffer, fmt, buflen);
buffer[buflen-1] = '\0';
// flockfile(stdout); // Multi-threading
printf(buffer, a);
va_list ap;
va_start(ap, fmt);
vprintf(p2, ap);
va_end(ap);
// funlockfile(stdout); // Multi-threading
}
The code cheats; it uses a C99 VLA (variable length array) and also interleaved declarations and statements. In C90, therefore, you'd probably end up using malloc(), or perhaps a fixed size buffer (running risks of overflows), or a hybrid (use a fixed size buffer if the prefix of the format string is small enough to fit, or allocate (and free) if not).
You need to decide on the appropriate error handling strategies. Note that this isn't bullet-proof code. Calling func(a, "%% yield is %d; data is [%s]\n", data); throws another spanner in the works. Also, if you're on POSIX and someone passes "it is %2$s and %1$d things happened", you're in deep trouble.
Yes, I'm on POSIX system. But it is quite old, I am even not able to use approaches with __VA_ARGS__ macros as it appears in C99+ standards.
It's curious that you're on such an old system. However, that simply means you probably won't write code with the n$ notations in it, so it is one less thing to worry about. The multi-threading issue is less likely to be a problem either. And you can't use the other C99 features that were shown in my sample code.
You could look at the FFI (foreign function interface) library; I'm not at all sure it will help, though.
Based on previous answer to parse format, I've found a simple solution (if %d is always first):
void func (int a, char *fmt, ...) {
char *new_format = malloc(10*sizeof(char));
snprintf(new_format, 10, "%d", a);
strcat(new_format, (fmt+2));
va_list ap;
va_start (ap, fmt);
vprintf(stdout, new_format, ap);
va_end(ap);
}
Special Thanks to Jonathan Leffler
I have the following code:
int __dmasprintf (char **s, const char *format, ...) {
char buf[512];
va_list arg;
int ret;
va_start(arg,format);
ret = vsprintf(buf, format, arg);
va_end(arg);
*s = strdup(buf);
if (*s == NULL) return -1;
return 0;
}
I want to add an argument to the va_list arg before calling vsprintf() because my format contains 1 extra argument at the end.
How to add an argument (for example char * myarg) to the the va_list arg?
Or is it possible to pass vsprintf() a customized list?
You can't.
You either need to make two vsprintf (surely vsnprintf?) calls, or replace your function with a variadic macro, like
#define __dmasprintf(S, FMT, ...) ( \
(*S = do_dmasprintf(FMT, __VA_ARGS__, my_arg)) == NULL ? -1 : 0)
char *do__dmasprintf (const char *format, ...) {
char buf[512];
va_list arg;
int ret;
va_start(arg,format);
ret = vsnprintf(buf, sizeof(buf), format, arg);
va_end(arg);
char *s = strdup(buf);
return s;
}
Notes:
I replaced vsprintf with vsnprintf. There's no reason to use the former here (or pretty much anywhere else)
you ignore ret. Should you?
I kept the macro argument layout similar to the original, but since __VA_ARGS__ must be one-or-more arguments (it can't be empty), that means at least one argument is required after FMT. Just remove the FMT argument entirely if you want to allow zero arguments after it.
There is unfortunately no direct way to do that. There is a reason for that : the stdarg macros take the address in the stack of the last known parameter, and then directly iterate the stack.
If you can use macros, #Useless provided a nice solution - beware, macros can have side effects when you pass variables pre- or post-fixed with ++ or --.
If you want to avoid macros, you will have to write your own variant of vsprintf. No problem for it, just find a source for C stdlib (GNU libc could be a nice start point) and be brave ... hope you can use macros !
I have a created a macro which will print the timestamp with all the prints.
void timestamp()
{
struct timeval tv;
gettimeofday(&tv,NULL);
printf("%d",tv.tv_sec );
}
#define printf_all(format, ...) { \
static const char format_string[] = format; \
printf(format_string, ##__VA_ARGS__); \
timestamp(); \
}
int main()
{
printf_all("%d\n",10);
return 0;
}
I want to convert this macro into a function. But I am facing problems while passing in the arguments.
void printf_timestamp(static const char format_string[]) {
static const char format_string[] = format;
printf(format_string, ##__VA_ARGS__);
timestamp();
}
You can use vprintf:
int printf_timestamp(const char* fmt, ...) {
va_list args;
int result;
va_start(args, fmt);
result = vprintf(fmt, args);
va_end(args);
timestamp();
return result;
}
This is usually done using the vprintf() function, as orlp explained. However, that alone leaves you with a format that cannot be typechecked: Usually, your compiler interpretes the format string literal in every printf() call that it sees to determine whether the corresponding arguments have the right types.
Some compilers allow you to add support for this kind of type checking, here is the __attribute__(()) that is needed for gcc:
//within header
void printfTimestamp(const char* format, ...)
__attribute__((format(printf, 1, 2)));
//within implementation file
void printfTimestamp(const char* format, ...) {
va_list args;
va_start(args, format);
int result = vfprintf(stderr, format, args);
va_end(args);
timestamp();
return result;
}
The arguments to the attribute are the argument number with the format string (1) and the first variable argument position (2). As such, if you were to add an additional parameter to your function, you would declare it like this:
void myPimpedPrintf(int foo, const char* format, ...)
__attribute__((format(printf, 2, 3)));
For just adding a timestamp, I agree that vprintf() as proposed by orlp is the way to go. However, I think there is a valid argument in favor of macros, and that's the use of other compiler built-in macros like __LINE__, __FILE__, and __func__, which I personally find very useful for logging.
I generally use something like this with gcc and anything else that does comma swallowing.
#define log_debug(M, ...) do{ \
struct timespec _ts; \
double _ts_sec; \
clock_gettime(CLOCK_MONOTONIC, &_ts);\
_ts_sec = _ts.tv_nsec * 1e-9 + _ts.tv_sec; \
fprintf(stderr, "%.3f [DEBUG] (%s:%d) " M, _ts_sec, \
__func__, __LINE__, ##__VA_ARGS__); \
}while(0)
// usage:
log_debug("x=%d\n", x);
There are a couple "tricks" going on here you might not be familiar with.
The first of which is the do{}while() loop. This is a fairly common and largely portable C idiom for writing multi-statement macros. It lets you use the macro terminating it in a ; and allows declaration of scoped variables. However, is a statement, not an expression, so there's no "return value". Usually doesn't matter for printf() like macros, but be aware, this can bite you in other uses when trying to make function-like macros.
The other "trick" is the use of comma swallowing with the '##' operator. Read more here: https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
Also, this takes advantage of the fact that back-to-back string literals are concatenated in C to form a single string literal. The downside is that this technique ONLY works with string literals, i.e. your format string cannot be in a variable.
I want to write a C function, which can take any number of arguments and print value of int arguments only. I saw va_list and va_arg in stdarg.h, but I couldn't find any mechanism to get the number of arguments. I cannot take first argument as the number of arguments.
There's no portable way in standard C to get the number of arguments in variable argument functions.
The common way to solve it is to use the rightmost parameter parmN to provide the number of integers explicitly. For instance, your function prototype may look like this:
int foo(int number, ...);
But this is not an option to you according to your question.
Reference: C FAQ: How can I discover how many arguments a function was actually called with?
There is no (portable) way to get the number of arguments in a variadic function at runtime. That number is lost at runtime (only the compiler knows it).
Notice that on common processors and application binary interfaces (ABI) convention (e.g. x86-64 ABI on Linux), the machine itself does not know at runtime the arity of the current call in C; this is also true for others x86 calling conventions. However, when the compiler is compiling a variadic call, it does know the arity at compile time (but does not emit it in the object file).
You could define the convention that your variadic function takes as first argument the number of remaining (variadic) arguments:
void print_integers (int nbints, ...);
Then you would implement it e.g.
#include <stdio.h>
#include <stdarg.h>
void print_integers (int nbints, ...)
{
va_list args;
va_start (nbints, args);
for (int ix=0; ix<nbints; ix++) {
int curarg = va_arg(args, int);
printf(" %d", curarg);
};
va_end(args);
fflush(NULL);
}
If you cannot do this, you have to customize your preprocessor or your compiler. Perhaps a job for your MELT extension (MELT is a domain specific language to extend GCC). Or perhaps use GPP as your preprocessor.
You could also play some cpp preprocessor tricks, notably stringification and concatenation and variadic macros. For instance, you might define
void print_integers_str(const char*, ...);
#define print_my_integers(...) \
print_integers_str(#__VA_ARGS__, ##__VA_ARGS__);
Then print_my_integers(x,y) gets expanded to print_integers_str("x,y", x,y) and your varadic function print_integer_str could parse the "x,y" literal string (at least count commas). But with print_my_integers(f(x,y),g(y)+/*,,*/k)you may get a nightmare.
To be short: I recommend give up that goal and do things otherwise.
Using NARGS macro (in this case limited to 8 args):
#include <stdio.h>
#include <stdarg.h>
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
#define fn(...) fn(NARGS(__VA_ARGS__), __VA_ARGS__)
static void (fn)(int n, ...)
{
va_list args;
int i;
va_start(args, n);
for (i = 0; i < n; i++) {
printf("%d\n", va_arg(args, int));
}
va_end(args);
}
int main(void)
{
fn(6, 7, 8);
return 0;
}
Or you can loop until -1:
#include <stdio.h>
#include <stdarg.h>
#define fn(...) fn(0, __VA_ARGS__, -1)
static void (fn)(int n, ...)
{
va_list args;
int i;
va_start(args, n);
while (1) {
i = va_arg(args, int);
if (i == -1) break;
printf("%d\n", i);
}
va_end(args);
}
int main(void)
{
fn(6, 7, 8);
return 0;
}