cc-preprocessor

variadic macro that references another variadic macro is not expanding the way I want


I have

#file debug.h
#ifndef PDEBUG
#define PDEBUG(msg ...)\
logger_write(logger_stdout, (struct log) {\
    .level_target = LOGLEV_DEBUG,\
    .message = msg,\
    .properties = strdict_make_from_va(__VA_ARGS__, 0)\
})
#endif
#include "err_multi.h"

and

#file err_multi.h
#ifdef PDEBUG
#define TRY(expr, ...) {\
maybe _err = (expr);\
switch(_err.code) {\
    case ERR_OK: break;\
        __VA_ARGS__ \
    case ERR_UNKNOWN:\
    default:\
        PDEBUG(\
            "Unhandled err, code {_err.code}: {_err.detail}",\
            "_err.code", _err.code,\
            "_err.detail", _err.detail\
        );\
        return _err;\
}\
}
#endif

This second file, err_multi.h, does not #include debug.h, which is why ifdef PDEBUG is there. err_multi.h does not have include guards, it's a file which only has #defines and is intended to be included multiple times to redefine the macros from site to site as desired. The file that #includes err_multi.h is allowed to provide (or not) its own PDEBUG.

Finally, I have debug.c which has the following:

#file debug.c
#include "debug.h"

void do_something(void) {
    TRY(get_log_message( &fmsg, msg, properties ));
}

(since debug.h defines PDEBUG, err_multi.h receives that definition)

debug.c is expanding to

{
  maybe _err = (get_log_message(&fmsg, msg, properties));
  switch (_err.code) {
  case ERR_OK:
    break;
  case ERR_UNKNOWN:
  default:
    (logger_write(
        logger_stdout,
        (struct log){.level_target = LOGLEV_DEBUG,
                     .message = "Unhandled err, code {_err.code}: {_err.detail}",
                     "_err.code", _err.code,
                     "_err.detail", _err.detail,
                     .properties = strdict_make_from_va(__VA_ARGS__, 0)}));
    return _err;
  }
}

The problem is on that last line there: When PDEBUG is expanding, __VA_ARGS__ is not getting replaced. What's up with that? Sorry, I know this is kind of a tangle of files.


struct log is:

#file debug.h
struct log {
    uint64_t level_target;
    char* message;
    struct strdict properties;
};

struct strdict is:

#file dict1.h
struct strdict {
    ptrdiff_t n;
    char **keys;
    char **vals;
};

struct strdict strdict_make_from_va(...);

Solution

  • #define PDEBUG(msg ...) needs to be #define PDEBUG(msg, ...). Note the comma

    Per the C standard:

    # define identifier lparen identifier-list , ... ) replacement-list new-line