c _Generic and macro funtion

Viewed 68

I'm writing DEBUG_MSG for print debug messages

#define DEBUG_MSG(msg_str) _DEBUG_MSG_GENERIC(msg_str)

The _DEBUG_MSG_GENERIC is because I'd like to:

  • Show int message when a input parameter is int
  • Show char* message when a input parameter is char*

and its implement:

#define _DEBUG_MSG_GENERIC(strs) \
  _Generic( (strs), \
             int: _DEBUG_MSG_INT, \
             default: _DEBUG_MSG_STR \
          )(strs)

Now I'd like to implement _DEBUG_MSG_INT and _DEBUG_MSG_STR with Macro function and printf :

#define _DEBUG_MSG_INT(val) printf("%d\n", val);
#define _DEBUG_MSG_STR(str) printf("%s\n", str);

But I got error message is:

main.c:14:30: error: ‘_DEBUG_MSG_INT’ undeclared (first use in this function); did you mean ‘DEBUG_MSG’?
   14 |                         int: _DEBUG_MSG_INT, \
      |                              ^~~~~~~~~~~~~~

How do I solve it?

Does _generic only support function(pointer to function) and not support macro function?

Full Code

#include <stdio.h>

#define DEBUG_MSG(msg_str) _DEBUG_MSG_GENERIC(msg_str)
#define _DEBUG_MSG_GENERIC(strs) \
            _Generic( (strs), \
                        int: _DEBUG_MSG_INT, \
                      default: _DEBUG_MSG_STR \
                    )(strs)
#define _DEBUG_MSG_INT(val) printf("%d\n", val)
#define _DEBUG_MSG_STR(str) printf("%s\n", str)

int main()
{
    DEBUG_MSG("str");
    DEBUG_MSG(5);
}
3 Answers

The problem is that both _DEBUG_MSG_INT and _DEBUG_MSG_STR are function-like macros thus they are only expanded if they are followed by ().

Note that macro expansion takes place before actual C compilation thus _Generic is nothing more than a common identifier at preprocessor stage.

I suggest using _Generic not for selection of the function pointer but rather for a formatting specifier to be used in printf(). Try:

#define _DEBUG_MSG_GENERIC(arg) printf( _DEBUG_MSG_FMTSPEC(arg), arg)
#define _DEBUG_MSG_FMTSPEC(arg) \
  _Generic( (arg), int: "%d\n", default: "%s\n")

I believe your issue is because the preprocessor only makes one pass of the source code, so the printf's don't get substituted.

A quick solution would be to define _DEBUG_MSG_INT(val) and _DEBUG_MSG_STR(str) as real functions like so:

void _DEBUG_MSG_INT(int val) {
    printf("%d\n", val);
}
void _DEBUG_MSG_STR(char * str) {
    printf("%s\n", str);
}

The compiler will optimise out the extra function call overhead and will behave as if you called printf directly.

_Generic is not a preprocessor operation and cannot be used to select preprocessor macro functions. The code after a : in its cases must be a C expression (specifically an assignment-expression).

The code you have in those positions is _DEBUG_MSG_INT and _DEBUG_MSG_STR. Those are preprocessor macro names.

Those preprocessor macros are function-like macros. They are macro-replaced only when they are followed by a (. In your code, there is no ( after them, so they are not replaced.

That means the code after reprocessing looks like int : _DEBUG_MSG_INT,. So the compiler attempts to interpret _DEBUG_MSG_INT as an expression. Since _DEBUG_MSG_INT is not a declared identifier, the compiler reports an error that it is undeclared.

In summary, your code _Generic( (strs), int: _DEBUG_MSG_INT, default: _DEBUG_MSG_STR )(strs) attempts to use an after-preprocessing _Generic selection to select a preprocessing-time macro (either _DEBUG_MSG_INT or _DEBUG_MSG_STR) and then to have that macro treated as a function-like macros with the (strs) that appears after the _Generic. That simply cannot work; an after-preprocessing _Generic cannot select preprocessing macro names.

Related