This might sound like an interview question but is actually a practical problem.
I am working with an embedded platform, and have available only the equivalents of those functions:
Furthermore, the printf()
implementation (and signature) is likely to change in the near future, so calls to it have to reside in a separate module in order to be easy to migrate later.
Given that, can I wrap logging calls in some function or macro? The goal is that my source code calls THAT_MACRO("Number of bunnies: %d", numBunnies);
in a thousand places, but calls to the above functions are seen only in a single place.
Compiler: arm-gcc -std=c99
Edit: just to mention, but post 2000 best practices and probably a lot earlier, inline functions are far better than macros for numerous reasons.
We can use printf() function in a Macro. In this example, we are creating a function like Macro that will print the result of a calculation, like adding two numbers.
In C, function-like macros are much similar to a function call. In this type of macro, you can define a function with arguments passed into it. TechVidvan Tutorial: Macros with arguments! In the above example, the compiler finds the name of the macro (AREA(a)) and replaces it with the statement (a*a).
Here we will see how to define a macro called PRINT(x), and this will print whatever the value of x, passed as an argument. To solve this problem, we will use the stringize operator. Using this operator the x is converted into string, then by calling the printf() function internally, the value of x will be printed.
"printf" is the name of one of the main C output functions, and stands for "print formatted". printf format strings are complementary to scanf format strings, which provide formatted input (lexing aka. parsing).
There are 2 ways to do this:
Variadric macro
#define my_printf(...) printf(__VA_ARGS__)
function that forwards va_args
#include <stdarg.h>
#include <stdio.h>
void my_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
There are also vsnprintf
, vfprintf
and whatever you can think of in stdio
.
Since you can use C99, I'd wrap it in a variadic macro:
#define TM_PRINTF(f_, ...) printf((f_), __VA_ARGS__)
#define TM_SNPRINTF(s_, sz_, f_, ...) snprintf((s_), (sz_), (f_), __VA_ARGS__)
since you didn't say that you have vprintf
or something like it. If you do have something like it, you could wrap it in a function like Sergey L has provided in his answer.
The above TM_PRINTF does not work with an empty VA_ARGS list. At least in GCC it is possible to write:
#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)
The two ## signs remove the excess comma in front of them them if __VA_ARGS__
is empty.
If you can live with having to wrap the call in two parentheses, you can do it like this:
#define THAT_MACRO(pargs) printf pargs
Then use it:
THAT_MACRO(("This is a string: %s\n", "foo"));
^
|
OMG
This works since from the preprocessor's point of view, the entire list of arguments becomes one macro argument, which is substituted with the parenthesis.
This is better than just plain doing
#define THAT_MACRO printf
Since it allows you to define it out:
#define THAT_MACRO(pargs) /* nothing */
This will "eat up" the macro arguments, they will never be part of the compiled code.
UPDATE Of course in C99 this technique is obsolete, just use a variadic macro and be happy.
#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)
The ##
token will enable the usage TM_PRINTF("aaa");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With