fprintf macro with single param expecting ')' - c

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

Related

C | Handling multiple arguments in macro function

I am trying to make a function similar to printf(), I want it to take multiple arguments so that I can use them when printing, roughly like the following:
void PRINT_RED(string, ...) {
fprintf(stderr, "\033[91m");
fprintf(stderr, "%s", string, __va_arg_pack());
fprintf(stderr, "\033[0m");
}
Take in consideration that I want it to have the following example API PRINT_RED("%s %s %d", string1, string2, int1);.
But as a macro, so I tried:
#define PRINT_RED(string, ...) \
fprintf(stderr, "\033[91m"); \
fprintf(stderr, "%s", string, __va_arg_pack()); \
fprintf(stderr, "\033[0m");
And apparently __va_arg_pack() can only be defined in inline functions... Can anybody guide me on how to handle multiple arguments in a macro function?
What you're looking for is the macro __VA_ARGS__, which translates to the variable arguments passed to a macro.
#define PRINT_RED(string, ...) \
fprintf(stderr, "\033[91m"); \
fprintf(stderr, "%s", string, __VA_ARGS__); \
fprintf(stderr, "\033[0m");

How to make a __VA_ARGS__ macro which can be called with no arguments? [duplicate]

This question already has answers here:
#define macro for debug printing in C?
(14 answers)
Closed 2 years ago.
I have a macro:
#define debug(fmt, ...) printf("%lu %s:%s:%i " fmt, ms(), __FILE__, __func__, __LINE__, __VA_ARGS__)
which does just what I want.
I can call it with:
debug("i: %i\n", i);
to print the value of i.
My problem is that I can't call it with:
debug("got here");
as that expands to:
printf("%lu %s:%s:%i %s " "got here", ms(), __FILE__, __func__, __LINE__,)
which is a trailing-comma bug.
How can I change my __VA_ARGS__ macro so that it can handle the "no variables"/"only format string" case?
You can do it in two steps:
#define debug(...) DEBUG(__VA_ARGS__, "")
#define DEBUG(fmt, ...) printf("%lu %s:%s:%i " fmt "%s", ms(), __FILE__, __func__, __LINE__, __VA_ARGS__)
debug("%d\n", 42);
debug("Hello\n");
In this way, even if you don't pass a second param it is replaced by an "" and results in a NOP.
Instead of trying to concatenate string literals in the macro itself, you can split the printf into two parts: One for the statistics, the other for your debug message. Yoke them together with the old do { ... } while (0) macro trick.
#define debug(...) do { \
printf("%lu %s:%s:%i ", ms(), __FILE__, __func__, __LINE__); \
printf(__VA_ARGS__); \
} while (0)
Then you don't need the fmt as separate argument and can just pass __VA_ARGS__ to the second printf.

How to stringify char buffer for C macro

I have a LOG(fmt, ...) macro that does not work when using a char buf[] as fmt.
The code below is a complete (not actually) working example of the code. In some_function(), I am trying to use LOG() in two different ways but only the first approach works.
To solve the problem, I have tried using #define LOG_STR(x) #x in the following way:
To stringify what is received in the #define LOG by applying LOG_STR() to format like this: LOG_STR(format); and
To apply LOG_STR() directly to the printing like this: LOG(LOG_STR(fmt), 6).
Neither approach works and in fact I get segfault out of it.
#include <stdio.h>
#define LOG(format, ...) do { \
fprintf(stderr, "[LOG] " format " [%s]\n", \
##__VA_ARGS__, __func__); \
} while (0)
static void some_function()
{
// This works
LOG("This is a number: %d", 5);
// This does not work
const char fmt[] = "This is a number: %d";
LOG(fmt, 6);
}
int main(void)
{
some_function();
return 0;
}
When I compile the code above I get the following error:
$ gcc -o log-macro-str log-macro-str.c
log-macro-str.c: In function ‘some_function’:
log-macro-str.c:15:6: error: expected ‘)’ before ‘fmt’
LOG(fmt, 6);
^
log-macro-str.c:4:29: note: in definition of macro ‘LOG’
fprintf(stderr, "[LOG] " format " [%s]\n", \
^~~~~~
I'd like to use LOG() in both ways as done in some_function() or without modifiers and just printing a string. I suspect I might have to stringify the format part but I can't seem to do it correctly.
What am I doing wrong, and how can I solve this issue?
The stringify operator, # in a macro, converts preprocessor tokens to text in a string literal. It will not change the contents of a char buffer to a compile-time string literal.
To make your macro work, use multiple fprintf statements:
#define LOG(format, ...) do { \
fprintf(stderr, "[LOG] "); \
fprintf(stderr, format, __VA_ARGS__); \
fprintf(stderr, " [%s]\n", __func__); \
} while (0)

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

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;
}

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.

Resources