Variadic Macro calling fprintf: how to add arguments to __VA_ARGS__? - c

I have two macros:
#define LogFunction(str) fprintf(stdout, "%s: %s\n",__FUNCTION__,(str))
#define LogPrintf(f_, ...) fprintf(stdout, (f_), ##__VA_ARGS__)
So i can use them this way:
void MyFunction()
{
int N=4;
LogFunction("START"); // Outputs "MyFunction: START"
LogPrintf("N=%d\n", N); // Outputs "N=4"
}
What I would like to change is to
add FUNCTION at the start of the LogPrintf as it is in LogFunction
add "\n" at the end of the LogPrintf without having to remember to put it in myself
so in the end i could have just one macro for my outputs.
I've tried to understand if Appending to __VA_ARGS__ could've been useful, but i admit that i've not understood if it is related to my case :(
Thanks.

why not doing it in 3 steps?
#define LogPrintf(f_, ...) do { fprintf(stdout, "%s: ",__FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fprintf(stdout,"\n"); } while(0)
this does 3 prints, but at least it's simple and does what you want. the do while(0) trick makes sure this is one only block (when using if without braces) and requires semicolon.

If you're willing to rely on the first argument to LogPrintf being a string literal, then you should be able to use string concatenation to achieve your objective:
// Assumes f_ always corresponds to a string literal:
#define LogPrintf(f_, ...) fprintf(stdout, "%s: " f_ "\n", __FUNCTION__, ##__VA_ARGS__)
Note, however, that in standard C, the LogPrintf macro requires at least two arguments, and the ## has no place. I keep it here only because you use it in your original code.
If you must accept format string expressions other than string literals, however, then your simplest alternative is to perform multiple I/O calls, as another answer also suggests:
#define LogPrintf(f_, ...) do { \
fprintf(stdout, "%s: ", __FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fputc('\n', stdout); \
} while (0)
Note that in this case, the macro expands to a statement (sans trailing semicolon), whereas in the other, the macro expands to an expression. If you want the return value(s) of any of the I/O functions, then you'll have to make special provisions for that in this case.
If that doesn't work for you either, then the ultimate alternative is to write and use a helper function, as was suggested in comments:
#define LogPrintf(f_, ...) log_printf_impl(stdout, __FUNCTION__, (f_), ##__VA_ARGS__)
int log_printf_impl(FILE *f, const char *func, const char *fmt, ...) {
static const char prefix[] = "%s: ";
size_t flen = strlen(fmt);
va_list args;
int result = -1;
char *aug_fmt = malloc(sizeof(prefix) + strlen(fmt) + 1);
if (aug_fmt) {
va_start(args, fmt);
sprintf(aug_fmt, "%s%s\n", prefix, fmt);
result = vfprintf(f, aug_fmt, func, args);
va_end(args);
free(aug_fmt);
}
return result;
}

Related

Variadic Macro for custom print function with argument expansion in C

I am trying replicate the printf for my application. Instead of using stdout, I have a different set of user specific pointers that basically point to the location one wants to print something. So instead of having:
#define print(fmt, ...) printf(stdout, fmt, __VA_ARGS__)
I want something like this:
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt, __VA_ARGS__)
Here the expectation is that the x is a struct that stores a users current context and information with it. I want to automatically expand x to use the pointer_to_a_screen which basically tells dev_printf where to print the given input. Expectation is the dev_printf needs to behave as any other standard printf function and the user can specify variable arguments.
Is this even possible? I keep getting a compile error for above #define print repeatedly and I cant understand why? I can't expand the first argument?
The trailing comma has to be removed in case no arguments are passed. Ie.
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt, __VA_ARGS__)
print(something, "arg: %d", i);
// expands to dev_printf(something->pointer_to_a_screen, "arg: %d", i);
// all fine
// but:
print(something, "no arg");
// expands to dev_printf(something->pointer_to_a_screen, "not arg", );
// ^^
In your case you can just:
#define print(x, ...) dev_printf(x->pointer_to_a_screen, __VA_ARGS__)
Newer code should use __VA_OPT__:
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt __VA_OPT__(,) __VA_ARGS__)
In days before __VA_OPT__ it was typical to use GNU extension ##__VA_ARGS__to:
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt, ##__VA_ARGS__)
You can also use POSIX fopencookie to create a custom stream that you could manipulate with normal fprintf functions.
Each of standard *printf function have equivalent v*printf function. The best is to provide your own dev_vprintf function that would take a va_list and then provide a simple wrapper:
#ifdef __GNUC__
// ex. on gcc compiler this causes printf-like warnings to happen
__attribute__((__format__(__printf__, 2, 3)))
#endif
return_type print(some_type *x, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
return_type e = dev_vprintf(x->pointer_to_a_screen, fmt, va);
va_end(va);
return e;
}
Such wrapper will check for type mismatches, is easy to maintain and it's easy to check for errors.
I would not use macro for that
int mystrangeprintf(MYTYPE *x, const char *fmt, ...)
{
int length;
char buff[256];
va_list va;
va_start(va, fmt);
length = vsnprintf(buff, sizeof(buff), fmt, va);
va_end(va);
dev_printString(x->pointer_to_a_screen, buff);
return length;
}

How to write a macro with optional and variadic arguments

I have a macro called PRINT(...) that I use in my code, which gets a variable number of arguments and acts like printf (gets a format and arguments). It's defined like this:
#define PRINT(...) PRINT(__VA_ARGS__)
Now I want to modify it so it will have an optional argument, say that its name is number and it will add a numeric prefix to the printing. For example:
PRINT("%s", "hi") -> will print hi
PRINT(1, "%s", "hi") -> will print 1: hi
How can I change the PRINT macro to support this?
Important to say, that I don't want to change any existing call to this macro from my code (in the example, if I have a call to PRINT("%s", "hi") - it needs to remain the same after the change).
Also, I can't create new macro for this purpose- must use the existing PRINT macro for this purpose (but off course I can change it's arguemnts definition).
Any idea how can I do this?
Edit: I saw this post about variadic macro- but It's different from what I'm asking here since the argument number needs to be a recognized variable, which will be treated in the implementation of PRINT as -1 if the call to PRINT doesn't contain the number argument (-1 will be an indicator for printing no number) and otherwise it will print the number prefix.
As of C11, you can use the _Generic keyword. This allows you to check the type of any value or variable. According to this document, _Generic has behaviour that varies between compilers. This answer provides a simple solution, though, using the comma operator.
#define PRINT(fst, ...) \
( \
_Generic((0, fst), char *: 1, default: 0) ? \
PRINTNL(fst, __VA_ARGS__) : \
PRINTL(fst, __VA_ARGS__) \
)
Where PRINTNL prints without the number and PRINTL prints with the number.
Rest of the code:
#define PRINTNL(...) printf(__VA_ARGS__)
#define PRINTL (n, ...) ({ \
printf("%d: ", n); \
printf(__VA_ARGS__); \
})
Since you'd know by the time of writing whether the first argument is a number prefix or not, make a macro by another name for prefixing with the number. Here I assume that PRINT(...) expands to printf(__VA_ARGS__):
#define PRINT(...) printf(__VA_ARGS__)
So define a macro NPRINT that calls printf twice, once to output the prefix with number and once with the format:
#define NPRINT(number, fmt, ...) (printf("%d: ", number), printf(fmt, __VA_ARGS__))
Usage
#include <stdio.h>
int main(void) {
NPRINT(1, "%s\n", "hi");
}
Of course this doesn't work if the call to printf was supposed to be atomic - now if the format string was always a literal string, then you could use string concatenation:
#define NPRINT(number, fmt, ...) (printf("%d " fmt, number, __VA_ARGS__))
If it can be a variable and only one call to PRINT is allowed, the only portable way that I could see is to make a function that builds the format.
With the latest edit that without the number argument, -1: should be prefixed, this would simply become:
#define PRINT(...) NPRINT(-1, __VA_ARGS__)
Please have a look at the ##__VA_ARGS__ macro. Also check the code below written for a log function.
In the header file
/**
* ##__VA_ARGS__ allows us to make varadic arguments optional
* https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html
* also check __VA_OPT__ (C++20)
*/
#define APP_LOG(message, ...) \
do { \
APP_LOGX( __FILE__, __LINE__, ("\e[1;34m[INFO]: \e[0m" message), ##__VA_ARGS__); /** BLUE */ \
} \
while(0)
#define APP_ERROR(message, ...) \
do { \
APP_LOGX( __FILE__, __LINE__, ("\e[1;31m[FATAL]: \e[0m" message), ##__VA_ARGS__); /** RED */ \
} \
while(0)
In the implementation
/** #include <libgen.h> */
void APP_LOGX(const char * file, int num, const char* message, ...)
{
va_list ap;
int length;
char * tfilename = NULL;
tfilename = strdup(file);
length = strlen(message);
if(length>0){
va_start(ap, message);
vprintf(message, ap);
printf(" | File: %s Line %d ", basename(tfilename), num);
va_end(ap);
/* add newline if nessasary */
if(message[length -1] != '\n'){
printf("\n");
}
}
free(tfilename);
}
Application
APP_LOG("app display init")
or
APP_LOG("Value of x acceleration %.2f", g);
APP_ERROR("Something bad happened!")

fprintf macro with single param expecting ')'

I got next debug macro, which works fine:
#ifndef NDEBUG
#define errorLog(fmt, ...) fprintf(stderr, "[ERROR %s: %d] " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
Except if i try next line (which produce expected a ')' error):
const char* someMessage = "Message";
errorLog(someMessage);
But this one produce no errors:
errorLog("Message");
Right now i solve this like:
const char* someMessage = "Message";
errorLog("%s", someMessage);
How to change macro to work with single const char* param as well?
const char* someMessage = "Message";
errorLog(someMessage); //fprintf(stderr, someMessage)
As written, your errorLog requires a string literal for the first (fmt) argument, as it tries to use string concatenation, which only works with string literals. If you want a macro that can take any char *, not just a string literal, you have to use it directly, rather than pasting. You could use something like:
#define errorLog(...) \
(fprintf(stderr, "[ERROR %s: %d] ", __FUNCTION__, __LINE__), \
fprintf(stderr, __VA_ARGS__), \
fprintf(stderr, "\n"))
This will work for any char *, but will not have the same return value, if you care about that. This also has the advantage that it doesn't depend on the gcc , ## __VA_ARGS__ extension which may not work on other compilers.
If you want this to work properly in a multithreaded program, you might need:
#define errorLog(...) \
(flockfile(stderr), \
fprintf(stderr, "[ERROR %s: %d] ", __FUNCTION__, __LINE__), \
fprintf(stderr, __VA_ARGS__), \
fprintf(stderr, "\n"), \
funlockfile(stderr))
instead

Issue with logging in C

I have many functions and while entering each function, I am calling a Macro FUNC_ENTRY, which is in turn calling the default logging macro (LOGGING).
To be specific, this is my FUNC_ENTRY macro:
#define FUNC_ENTRY LOGGING(ONE, "Entry");
My LOGGING macro is defined as below:
#define LOGGING(prio, my_var, ...) \
{\
char priority[6];\
bzero(level,4); \
if (prio == ONE) { sprintf(priority,"ONE");} \
if (prio == TWO) { sprintf(priority,"TWO");} \
if (prio == THREE) { sprintf(priority,"THREE");} \
fprintf(fp,"%s: %s: %s: %d: %s:%s|\n",__DATE__, __TIME__, __FILE__,__LINE__, __func__,level);\
fflush(fp); \
fprintf(fp,my_var,##__VA_ARGS__);\
fflush(fp); \
fprintf(fp,"\n");\
fflush(fp); \
}
OK, now my question is, whenever I enter any function, my LOGGING macro should print "Entry to xyz function". As of now, it only prints "Entry". Any clue how to achieve this?
The way I am calling the FUNC_ENTRY macro is as below.
Suppose I have a function xyz;
void xyz {
FUNC_ENTRY;
/*other statements*/
}
There are a number of issues:
That's two too many fflush(fp) calls for most purposes, unless you think you often write buggy code.
'Tis odd that you zero level, declare and set priority, but print level which is still all zeros.
That fragment is big enough to warrant a function.
You don't want the semi-colon after #define FUNCENTRY.
'Tis also odd that you think you should get Entry to xyz function when you don't include to or function in the strings in the macro.
It also seems pointless reporting the time and date when the file was compiled in your error messages (which is what __DATE__ and __TIME__ are for).
Fixing some of those issues leads to:
#define FUNC_ENTRY LOGGING(ONE, "Entry to %s function\n", __func__)
#define LOGGING(prio, my_var, ...) \
{\
char priority[6];\
memset(priority, '\0', sizeof(priority)); \
if (prio == ONE) { sprintf(priority, "ERR");} \
if (prio == TWO) { sprintf(priority, "TWO");} \
if (prio == THREE) { sprintf(priority, "THREE");} \
fprintf(fp,"%s: %s: %s: %d: %s:%s|\n", __DATE__, __TIME__, __FILE__, __LINE__, __func__, priority);\
fprintf(fp, my_var, ##__VA_ARGS__); \
fprintf(fp,"\n"); \
fflush(fp); \
}
I note that , ##__VA_ARGS__ is a GCC extension over standard C. The bzero() function was marked obsolescent in POSIX 2004 and is missing from POSIX 2008; it is best not to use it.
It is not clear, but judging from the code, you have a global (or, at least, file scope) variable FILE *fp; which is initialized to the log file you want to write to. Ick; and likewise yuck!
Were it my code (and I wanted the Entry to xyz function message spelled thus), I'd probably use:
extern void err_logger(int level, int line, char const *file, char const *func, ...);
and
#define FUNC_ENTRY LOGGING(ONE, "Entry to %s function\n", __func__)
#define LOGGING(prio, ...) \
err_logger(level, __LINE__, __FILE__, __func__, __VA_ARGS)
And the implementation of err_logger() might be:
void err_logger(int level, int line, char const *file, char const *func, ...)
{
char *priority = "";
if (level == ONE) priority = "ERR";
if (level == TWO) priority = "TWO";
if (level == THREE) priority = "THREE";
time_t t = time(0);
char date_time[32];
strftime(date_time, sizeof(date_time), "%Y-%m-%d %H:%M:%S", gmtime(&t));
fprintf(logfp, "%s: %s: %d: %s: %s|\n", date_time, file, line, func, priority);
va_list args;
va_start(args, func);
char *fmt = va_arg(args, char *);
vfprintf(logfp, fmt, args);
va_end(args);
fflush(logfp);
}
Where logfp would be the log file stream and would be private to the file that holds the source for err_logger().
I've not compiled that code; there could be minor errors in it. It does, however, give you the general idea of what to do and how to do it — I think.
Something along these lines:
#include <stdio.h>
#define T printf("Entering %s\n", __func__)
int main(void)
{
T;
return 0;
}
Unfortunately it looks like you're not going to be able to concatenate that to your string at compile time (see here: Can I substitute __func__ into an identifier name in a C macro?). There may be some other compiler specific or C++ specific tricks but otherwise you will need to build your string at run time.

How to convert a variable argument function into a macro?

I have a variable argument function which prints error messages in my application, whose code is given below:
void error(char *format,...)
{ va_list args;
printf("Error: ");
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
printf("\n");
abort();
}
This function is used in error conditions as follows:
error("invalid image width %d and image height %d in GIF file %s",wid,hei,name);
The error() function is called from different places with different arguments (variable argument function).
The function approach works fine.
Now, if I have to convert this function into a macro, how do I do it? I tried doing it as:
#define error(format) {va_list args;\
printf("Error: ");\
va_start(args, format);\
vfprintf(stderr, format, args);\
va_end(args);\
printf("\n"); abort()}
But this does not print the arguments correctly.
What is wrong in the macro definition above?
What is the fix?
If your compiler supports ISO style variadic macros, you can define a macro as such:
#define error(...) \
fprintf(stderr, "Error: "); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
abort();
Alternatively, if you are using GCC, there's also the GNU style variadic macro used as such:
#define error(format, args...) \
fprintf(stderr, "Error: "); \
fprintf(stderr, format , ## args); \
fprintf(stderr, "\n"); \
abort();
For more information, see http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
Update
If your compiler does not have variadic macro support, a (poor?) alternative would be to stick to the va_list in function approach. If you wish the definition to reside in a header file, then perhaps a static inline function?
static inline void error(const char *fmt, ...) {
#define PRINT_ERROR
va_list args;
fprintf(stderr, "Error: ");
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
abort();
#endif
}
Here is an article with some examples on variable arguments used in a macro. It looks like it should do what you're looking for. You can use __VA_ARGS__ in your macro.
Which compiler are you using?
Macros do not (yet?) support variadic arguments and anyway, using va_list wouldn't work here, it only works on variadic function arguments.
Why do you want to replace the function (which works fine, according to you) with a macro anyway?
There's a common extension that does what you want, simply write:
#define error(args...) (fputs("Error: ",stdout),printf(args),puts(""))
C99 users can also say:
#define error(...) (fputs("Error: ",stdout),printf(__VA_ARGS__),puts(""))
but there are some problems with using __VA_ARGS__. Fortunately there's a GCC extension to deal with it, but then you're back to using a compiler-specific extension, and the args... mode is more widely available.
Many compilers support GNU style variadic macros, like this:
#define error(format,args...) do { \
fprintf(stderr, "error: " format "\n", ##args); \
abort(); \
} while(0)
However, if you aim for portability, do not use variadic macros.

Resources