8

I know that variadic macros have been added in C99 (and via GNU extensions). I've been wondering if there is a nice alternative in ANSI C.

I've come up with something like this, but it's still kind of awkward:

void log_info(const char *file, int line, const char *fmt, ...)
{
#ifdef DEBUG
    va_list ap;
    char newfmt[1024] = { 0 };
    va_start(ap, fmt);
    sprintf(newfmt, "[INFO] (%s:%d): %s\n", file, line, fmt);
    vfprintf(stderr, newfmt, ap);
    va_end(ap);
#endif
}

So that this can be called like this:

log_info(__FILE__, __LINE__, "info message: %s %d", "helloworld", 12);

There is nothing wrong with this approach, however I'm wondering if there is a nicer way of doing it? Eg. with no need to specify file/line everytime.

I'd appreciate any feedback. :)

Edit: By ANSI C here I mean C89.

Edit: The answer below is fine but I believe given it requires running the printing command twise it may impose some thread-safety issues as it is. Another alternative might be to use define to minimize the typing (also quite ugly):

#define __FL__ __FILE__, __LINE__

and then run the command like:

log_info(__FL__, "info message: %s %d", "helloworld", 12);
10
  • 1
    In what situation could you possibly be writing C with a compiler that doesn't support variadic macros? Commented Dec 26, 2014 at 23:40
  • 2
    @Alexis King: Thanks for the response. Say for example when C89 is a requirement by a company standards or a particular project? I know most of the compilers would still compile the code even with -ansi flag and my question is not about why I should care but rather what would/should I do if I am under such constraints but still need the functionality. Its more of a curiosity question, I must admit.
    – snobb
    Commented Dec 26, 2014 at 23:43
  • 2
    Please use snprintf(newfmt, sizeof(newfmt), "…", …) to prevent buffer overruns. Though (on second thoughts), I suppose that if you don't have C99 you may not have snprintf() either. That's not quite true — MSVC is mostly C89 but the libraries include snprintf(). At least, use snprintf() if you possibly can. Commented Dec 27, 2014 at 0:07
  • 4
    "ANSI C" is a misnomer. It commonly refers to the language described by the 1989 ANSI C standard (which is the same language described by the 1990 ISO C standard), but in fact ANSI has adopted each edition of the ISO C standard (1990, 1999, 2011) shortly after its publication. Commented Dec 27, 2014 at 0:37
  • 1
    You should probably look at the discussion in C #define macro for debug printing Commented Dec 27, 2014 at 2:04

2 Answers 2

19

It's a little ugly, but a sequence of comma-separated expressions in parentheses can be treated as a single argument.

An example:

#include <stdio.h>
    
#define LOG(args) (printf("LOG: %s:%d ", __FILE__, __LINE__), printf args)
    
int main(void) {
    int n = 42; 
    LOG(("Hello, world\n"));
    LOG(("n = %d\n", n));
    return 0;
}   

The output:

LOG: c.c:6 Hello, world
LOG: c.c:8 n = 42

Note that this requires an extra set of parentheses in the call.

The preprocessed code will look like:

#include <stdio.h>

int main(void)
{   
    int n = 42; 
    (printf("LOG: %s:%d ", "test_variadic.c", 7), printf ("Hello, world\n"));
    (printf("LOG: %s:%d ", "test_variadic.c", 9), printf ("n = %d\n", n));
    return 0;
}
3
  • Thanks. It's interesting.
    – snobb
    Commented Dec 27, 2014 at 0:45
  • 1
    The only caveat with this approach is that you MUST run printf (or any other printing function) twice which I reckon may impose some issues in multi-threaded applications.
    – snobb
    Commented Dec 27, 2014 at 11:02
  • 1
    @Alex I didn't understand what you meant by 2 printf() calls, when the output shows only one message. But I see that the first printf() call basically prints half the message, then the second prints the rest. I edited the answer to put the preprocessed code. One thing to note, the variable declaration of int n after a statement is not C89, and the goal of this is for C89 (because it doesn't support variadic macros).
    – Rich Jahn
    Commented Feb 24, 2023 at 13:56
5

Here is an even uglier option, but uses single call to printf:

#define PROMPT "[INFO] (%s:%d): "
#define FL __FILE__, LINE__

#define DEBUG0(fmt) printf(PROMPT fmt, FL);
#define DEBUG1(fmt, a1) printf(PROMPT fmt, FL, a1);
#define DEBUG2(fmt, a1, a2) printf(PROMPT fmt, FL, a1, a2);
#define DEBUG3(fmt, a1, a2, a3) printf(PROMPT fmt, FL, a1, a2, a3);

and so on until the most number of arguments that you call the macro with.

2
  • Thanks for this. I firstly thought it's rather awkward but the more I think about it the more I like the solution. The only bad side of it is the need to create a LOT of defines.
    – snobb
    Commented Jan 2, 2015 at 0:31
  • 2
    @Alex yeah , it's bad to set up but once it's all hidden in a header it's not so bad in your other source files
    – M.M
    Commented Jan 2, 2015 at 11:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.