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);
M.M
  • 134,614
  • 21
  • 188
  • 335
Alex
  • 838
  • 1
  • 13
  • 16
  • In what situation could you possibly be writing C with a compiler that doesn't support variadic macros? – Alexis King Dec 26 '14 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. – Alex Dec 26 '14 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. – Jonathan Leffler Dec 27 '14 at 00:07
  • @Jonathan Leffler: Yes, I totally agree and I would have used snprintf but as you quite correctly pointed out - it's also C99. I doubt though the message would ever be longer then 1024 (not intentionally anyway ;) ). I believe 1024 is a message size limit in syslog as well. – Alex Dec 27 '14 at 00:32
  • 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](http://ansi.org/) has adopted each edition of the ISO C standard (1990, 1999, 2011) shortly after its publication. – Keith Thompson Dec 27 '14 at 00:37
  • @KeithThompson: Thanks for the comment. I agree and indeed by ANSI C I meant C89. – Alex Dec 27 '14 at 00:41
  • 1
    You should probably look at the discussion in [C `#define` macro for debug printing](http://stackoverflow.com/questions/1644868/c-define-macro-for-debug-printing/1644898#1644898) – Jonathan Leffler Dec 27 '14 at 02:04
  • @JonathanLeffler: thanks for the link. It's interesting. – Alex Dec 27 '14 at 08:49

2 Answers2

17

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) {
    LOG(("Hello, world\n"));
    int n = 42;
    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.

Keith Thompson
  • 242,098
  • 41
  • 402
  • 602
  • Thanks. It's interesting. – Alex Dec 27 '14 at 00: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. – Alex Dec 27 '14 at 11:02
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.

M.M
  • 134,614
  • 21
  • 188
  • 335
  • 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. – Alex Jan 02 '15 at 00: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 Jan 02 '15 at 11:26