Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap printf() into a function or macro?

Tags:

c

logging

c99

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:

  • printf()
  • snprintf()

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.

like image 467
Vorac Avatar asked Dec 17 '13 16:12

Vorac


People also ask

Is printf a macro?

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.

Can we use macro for function in C?

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).

How do I print a macro value?

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.

How is printf defined?

"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).


4 Answers

There are 2 ways to do this:

  1. Variadric macro

    #define my_printf(...) printf(__VA_ARGS__)
    
  2. 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.

like image 149
Sergey L. Avatar answered Sep 25 '22 05:09

Sergey L.


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.

like image 39
ldav1s Avatar answered Sep 23 '22 05:09

ldav1s


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.

like image 11
unwind Avatar answered Sep 23 '22 05:09

unwind


#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)

The ## token will enable the usage TM_PRINTF("aaa");

like image 11
Felipe Lavratti Avatar answered Sep 25 '22 05:09

Felipe Lavratti