Good Verbosity Macro (C99) - c

I'm looking to write what I would imagine is a fairly common macro. I want to emulate the repeated "-v" options on many POSIX programs by defining a bunch of macros of the following form:
#define V1(str, ...) if(optv >= 1){printf("%s: "str,prog,__VA_ARGS__);}
int main(int argc, char* argv[])
{
// ... stuff ...
int i = 1;
V1("This contains a variable: %d\n",i);
}
// Output:
// ./program: This contains a variable: 1
where optv counts the number of "-v" options found on the command line and prog contains the program name (neither shown). This works well, but the problem is that I have to use a variable. V1("Output") will generate a compiler error. I could always use V1("Output%s","") but there should be a cleaner solution.

The GNU C preprocessor has a special feature that lets you delete the trailing comma when there are no arguments filling the variadic portion by prepending the token-pasting operator ## to __VA_ARGS__:
#define V1(str, ...) if(optv < 1); else printf("%s: "str,prog, ## __VA_ARGS__)
Alternatively, if you wish to remain fully C99 compliant, you could incorporate the the format string parameter into the ellipsis, but in this instance you'll also need to refactor your code since you want to include the extra prog parameter between the format string and the varargs. Something like this might work:
#define V1(...) if(optv < 1); else myprintf(prog, __VA_ARGS__)
int myprintf(const char *prog, const char *fmt, ...)
{
// Print out the program name, then forward the rest onto printf
printf("%s: ", prog);
va_list ap;
va_start(ap, fmt);
int ret = vprintf(fmt, ap);
va_end(ap);
return ret;
}
Then, V1("Output") expands to myprintf(prog, "Output") without using any non-C99 compiler extensions.
EDIT
Also note that I inverted the if condition in the macro, due to some weird issues that can arise if you invoke the macro inside an if statement without braces—see this FAQ for a detailed explanation.

Why don't you use 2 different macros for each verbosity level; one which prints a message and variable, and one which just prints a message?

You should probably write yourself a small support function so that you can do the job cleanly:
extern void vb_print(const char *format, ...);
#define V1(...) do { if (optv >= 1) vb_print(__VA_ARGS__); } while (0)
I assume that both optv and prog are global variables. These would go into a header (you wouldn't write them out in the programs themselves, would you?).
The function can be:
#include <stdio.h>
#include <stdarg.h>
extern const char *prog;
void vb_print(const char *format, ...)
{
va_list args;
va_start(args, format);
printf("%s:", prog);
vprintf(format, args);
va_end(args);
}
There's no rocket science in there. You can tweak the system to your heart's content, allowing a choice of where the information is written, flushing the output, ensuring there's a newline at the end, etc.

Try this:
#define V1X(str, ...) if(optv >= 1) {printf("%s: "str,prog,__VA_ARGS__);} else
#define V1(...) V1X(__VA_ARGS__,0)
I believe that fixes the problem you described, and the else at the end fixed another problem.

Related

Is there a C macro that replaces varied length functions/strings (regex) in file?

I want to be able to:
#define DEBUG_MODE 1
In order to turn on and off the printf() function calls inside all of my code. I know how to do this if it's set up as such:
if (DEBUG_MODE) printf("Hello World\n");
However, that takes foresight and setting it up line by line.
Is there like a find and replace feature within macros that exists or can be built that can replace a given expression - like printf(); - with an empty string (or anything else you might want)?
#define DEBUG_MODE replace("printf(some_regular_expression);", ""); // where "" is empty string
I'm sure this is not possible let alone with regex, but it doesn't hurt to ask.
While such a beast could be constructed by various shenanigans, I would strongly advise against it. It's not available in standard C and your IDE will hate you.
The typical method is defining something like debug_printf to be either printf (or more recently, fprintf(stderr, ...)) or a macro that expands to nothing.
I recommend tolerating the extra level of indirection and just going ahead and writing
#include <stdio.h>
#include <stdarg.h>
void debug_printf(const char *fmt, ...)
{
#if !defined(DEBUG)
if (loglevel < LOG_DEBUG) return;
#endif
va_list ap;
va_start(ap, &fmt);
vfprintf(stderr, fmt, ap);
}
where loglevel is a global variable and LOG_DEBUG is a macro and both are defined by you. The idea is you can turn these statements back on at runtime by passing a parameter to the program. This is often useful if a significant amount of work has been put into the debug prints.
Once having done so, you can use your IDE search & replace once to update all the call sites that your regex would have matched.

C: passing arguments from variadic function to variadic macro

I have a standard logging API built into my C code, a nice simple logF(const char *pFormat, ...) thing which has always, so far, been mapped to vprintf(), i.e.:
void logF(const char *pFormat, ...)
{
va_list args;
va_start(args, pFormat);
vprintf(pFormat, args);
va_end(args);
}
The C code above this API must work on multiple embedded platforms. I've just reached a platform (Nordic NRF52840) where the underlying logging interface I have to work with is presented as a variadic macro of the form NRF_LOG_INFO(...).
QUESTION: how do I correctly pass fn(const char *pFormat, ...) into a BLAH(...) macro? My brain hurts....
This is with GCC 4.9.3, though it would be nice to have a solution that is relatively relaxed about C compiler version.
EDIT 1: noted that I could redefine my logF() function to be a variadic macro and map it there, the issue is that I would then have a platform-specific header file rather than a generic one, I would have to move it down into the platform code and have one for each. Not impossible but more messy.
EDIT 2: I was asked for the trail of how NRF_LOG_INFO() expands. Here's the relevant output of the pre-processor:
#define NRF_LOG_INFO(...) NRF_LOG_INTERNAL_INFO( __VA_ARGS__)
#define NRF_LOG_INTERNAL_INFO(...) NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO, __VA_ARGS__)
#define NRF_LOG_INTERNAL_MODULE(level,level_id,...) if (NRF_LOG_ENABLED && (NRF_LOG_LEVEL >= level) && (level <= NRF_LOG_DEFAULT_LEVEL)) { if (NRF_LOG_FILTER >= level) { LOG_INTERNAL(LOG_SEVERITY_MOD_ID(level_id), __VA_ARGS__); } }
#define LOG_INTERNAL(type,...) LOG_INTERNAL_X(NUM_VA_ARGS_LESS_1( __VA_ARGS__), type, __VA_ARGS__)
#define LOG_INTERNAL_X(N,...) CONCAT_2(LOG_INTERNAL_, N) (__VA_ARGS__)
Then depending on number of args, anything up to:
#define LOG_INTERNAL_6(type,str,arg0,arg1,arg2,arg3,arg4,arg5) nrf_log_frontend_std_6(type, str, (uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2), (uint32_t)(arg3), (uint32_t)(arg4), (uint32_t)(arg5))
void nrf_log_frontend_std_6(uint32_t severity_mid,
char const * const p_str,
uint32_t val0,
uint32_t val1,
uint32_t val2,
uint32_t val3,
uint32_t val4,
uint32_t val5);
It is not possible to pass the arguments from a variadic function to a variadic macro.
As you want to hide the platform specific macro call from the API header you can process the function arguments with vsnprintf instead of vprintf and call the logging macro with format "%s" and the resulting string buffer.
void logF(const char *pFormat, ...)
{
va_list args;
/* Choose a reasonable size or replace with dynamic allocation based on the return value of vsnprintf */
/* This could also be a static variable or a global variable to avoid allocation of a big buffer on the stack. */
char buffer[1024];
va_start(args, pFormat);
vsnprintf(buffer, sizeof(buffer), pFormat, args);
NRF_LOG_INFO("%s", buffer);
va_end(args);
}
Note that you may have to call NRF_LOG_FLUSH before the buffer goes out of scope or gets overwritten. See https://devzone.nordicsemi.com/f/nordic-q-a/22647/nrf_log_info-how-to-print-log-with-string-parameter

How to define a c macro with multiple statement

I am trying to define a macro that has two line/statements, it's like:
#define FLUSH_PRINTF(x) printf(x);fflush(stdout);
but it can't work due to the limit that C macros cannot work with ';'.
Is there any reasonable way to work around it?
P.S.: I know the upper example is weird and I should use something like a normal function. but it's just a simple example that I want to question about how to define a multiple statement Macro.
This is an appropriate time to use the do { ... } while (0) idiom.
This is also an appropriate time to use variadic macro arguments.
#define FLUSH_PRINTF(...) \
do { \
printf(__VA_ARGS__); \
fflush(stdout); \
} while (0)
You could also do this with a wrapper function, but it would be more typing, because of the extra boilerplate involved with using vprintf.
#include <stdarg.h>
#include <stdio.h>
/* optional: */ static inline
void
flush_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fflush(stdout);
}
Use the multiple expression in macro
#define FLUSH_PRINTF(x) (printf(x), fflush(stdout))

Programming with verbose option in C

Does someone know how to write a program in C with the verbose option (the option to choose if messages are printed or not) in a nice way.
I mean, not writing an if(verbose) for each printf in the code.
Is there a more elegant solution?
Just use a (variadic) macro / vararg function that tests a flag before calling something like vfprintf.
/* Crude example. */
void my_log(char *format, ...)
{
va_list args;
if (!ok_to_log)
return;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
EDIT As requested
How about a slightly modified gnu example:
#define eprintf(format, ...) do { \
if (ok_to_print) \
fprintf(stderr, format, ##__VA_ARGS__); \
} while(0)
Make an array of function pointers
print_function_type fx[2] = {quietprint, verboseprint};
and instead of using an if when printing, use the correct element of the array
// if verbosemode is 0 call quietprint
// if verbosemode is 1 call verboseprint
fx[verbosemode]("%d", foo);
You can write your own printf-like function that checks for the verbose flag and then calls printf, if necessary.

C puzzle...How Can I pass variadic arguments into a macro?

I got stuck here...
#include <stdio.h>
#define DBG_LVL(lvl, stmt) \
do{ \
if(lvl>1) printf stmt; \
}while(0)
#define DBG_INFO(stmt) DBG_LVL(1, stmt)
#define DBG_ERROR(stmt) DBG_LVL(2, stmt)
int main()
{
DBG_INFO(("hello, %s!\n", "world"));
DBG_ERROR(("crazy, %s!\n", "world"));
return 0;
}
As you can see, the code above uses macros like "DBG_INFO" or "DBG_ERROR" to control debug information level.
Now for some reason, I have to replace DBG_LVL() with a new function.
void myprint(int lvl, const char * format, ...);
The only difference is the debug level is taken as its fisrt parameter.
I was thinking:
#define DBG_LVL(lvl, stmt) myprint(lvl, stmt)
Of course it failed, because the "stmt" expression includes parentheses around.
Then I googled around trying to find a way to strip the parentheses, seems there's nothing could help.
I also tried some tricks to pass parameters into "stmt", still failed... :(
Can you help me?
# define EXPAND_ARGS(...) __VA_ARGS__
# define DBG_LVL(lvl, stmt) myprint(lvl, EXPAND_ARGS stmt);
Don't write this as a macro.
Write instead an ordinary varargs function:
void DBG_LVL(int level, char *fmt, ...)
{
if (level < 1) return;
va_list args;
va_start(args, fmt);
vaprintf(fmt, args);
va_end(args);
}
For myprint(), define a similar vamyprint(int lvl, const char *format, va_list ap) as well, and forward the same way.

Resources