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

88,676

Solution 1

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.

Solution 2

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.

Solution 3

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

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

Solution 4

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.

Solution 5

#define PRINTF(...) printf(__VA_ARGS__)

This works like this:

It defines the parameterized macro PRINTF to accept (up to) infinite arguments, then preprocesses it from PRINTF(...) to printf(__VA_ARGS__). __VA_ARGS__ is used in parameterized macro definitions to denote the arguments given ('cause you can't name infinite arguments, can you?).

Share:
88,676

Related videos on Youtube

Vorac
Author by

Vorac

Coding is like computer games except sometimes it's useful.

Updated on June 12, 2021

Comments

  • Vorac
    Vorac about 3 years

    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.

    • alk
      alk over 10 years
      Does your compiler support variadic macros?
    • ldav1s
      ldav1s over 10 years
      What compiler restrictions are in place? If this must run on a version of C before C99, it will be hard to accomplish portably as a macro.
    • Roddy
      Roddy over 10 years
      @KerrekSB I thought WHYT? comments got blocked these days?
  • jmlemetayer
    jmlemetayer over 10 years
    Moreover, you can find some documentation about macros (and variadic macros) here.
  • Sergey L.
    Sergey L. over 10 years
    @Roddy Yes if you want to forward all arguments. I would discourage it as you can't define a no-op macro in that way. With a function-like macro you can always make it no-op if you have to.
  • Vorac
    Vorac over 10 years
    I wish someone edited this answer, so that I can remove my upvote. I DO NOT have vprintf or other fancies. Embedded, you know.
  • Vorac
    Vorac over 10 years
    TM_PRINTF("Starting ei-oh!"); yields error: expected expression before ')' token. Without the format string argument it works. Do variadic arguments need to be non-zero in number?
  • Vorac
    Vorac over 10 years
    OMG thanks for the great answer. I guess you'll be getting upvotes from all the poor C89 programmers out there over the next several years :)
  • Vorac
    Vorac over 10 years
    It look that the above error cannot be fixed without gcc extensions: gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
  • Vorac
    Vorac over 10 years
    Sorry for the tone. I really can't use the standard libraries for this. It's a custom ARM-based platform.
  • natenho
    natenho over 8 years
    It's like magic! Saved me a lot of duplicated macros for each case! How does that ## work?
  • Alice
    Alice about 8 years
    @ΈρικΚωνσταντόπουλος Yes and that's one of the very few systems where ASM would be an acceptable trade off of programmer time.
  • drmuelr
    drmuelr almost 8 years
    An important side effect of this technique: all calls to THAT_MACRO will need double parentheses, even with single argument calls, e.g. THAT_MACRO(("Foo Bar")). --With love, a poor C89 programmer from several years later.
  • Lesto
    Lesto almost 8 years
    You can solve the issue removing the _f part and keeping only the varidac thing. Work without issue for me but I don't know if it is out of standard
  • ldav1s
    ldav1s almost 8 years
    @lesto, yes it "works", but removing f_ would make TM_PRINTF() allowable. If that string were in the code, the compiler would complain later about about printf() which doesn't match it's function signature. It's better to make the compiler complain about TM_PRINTF in this case, since that's more visible.
  • John Strood
    John Strood over 7 years
    @ldav1s I didn't get what you meant by a "variadic" macro. How did magically those ellipsis ... forward them to __VA_ARGS__ ?
  • ldav1s
    ldav1s over 7 years
    @Djack a variadic macro is one that uses the ... notation in the #define parameters list and usually __VA_ARGS__ in the replacement text. The C preprocessor (especially before C99) isn't as sophisticated as other languages, barely more than just simple text replacement. In this case it's got to do more than simple text replacement -- it's got to buffer up all the tokens when the macro gets invoked. For example, in the invocation TM_PRINTF(foo, bar, baz) the preprocessor sets a replacement variable f_ to the token foo and __VA_ARGS__ to the tokens bar, , and baz.
  • ferdepe
    ferdepe over 7 years
    Should it work with cygwin? Because I'm not able to make it works! However, the second way of @Sergey L. worked.
  • ldav1s
    ldav1s over 7 years
    @ferdepe You need to be using at least C99 to compile using this syntax (.e.g. gcc -std=c99) otherwise it won't work. The cygwin should work if it has C99 support.
  • sudo
    sudo over 7 years
    Yeah, this worked while the accepted answer did not. Thanks!
  • Jostikas
    Jostikas about 7 years
    Note that this is a compiler extension, not C99 standard. Basically, compiler writers, smart people that they are, recognised this oversight in the standard, and found a workaround. The caveat is that it's not guaranteed to work on every C99 compliant compiler, and some compilers might use different syntax for the same thing.
  • Apache
    Apache over 4 years
    I've been trying various methods like this, but 1. I can't make Macros that work through namespaces; #define Log::WriteLine(_Format, ...) printf(_Format, __VA_ARGS__) doesn't work. And 2. It doesn't show the %s etc. in lime green, or validate input... and is therefore useless as a substitute. If there any way to get a custom printf that shows the %s etc. in lime green, with the validation that is essential for use?
  • Sergey L.
    Sergey L. over 4 years
    @Apache This question was asked with respect to C, not C++. In C++ you should use a variadic template to forward template <typename... T> auto WriteLine(const char * _Format, T &&... args) { return printf(_Format, std::forward<T>(args)...); }
  • Apache
    Apache over 4 years
    @SergeyL.Will that show the syntax highlighting correctly as well, with the %s etc, in lime green? That's the essential thing. Without it, it's useless to mask the printf function.
  • ThorSummoner
    ThorSummoner about 4 years
    why is fmt an argument of va_start?
  • ThorSummoner
    ThorSummoner about 4 years
    oh, I'm guessing but I think va_start's "last" argument means the last stack arguments when examined right-to-left, eg "stop parsing va args when we get to fmt"