Reputation: 1209
I'd like to make _Generic
evaluate another macro. In the example below, db_put_u8
will insert a uint8_t
into the buffer and while at it, it will also leave a log message. The log helps me understand if I missed a field. Hence the macro. I have reduced the functionality to illustrate the problem here, but in practice it's supposed to have for various data types.
#include <stdio.h>
#include <stdint.h>
#define db_put_u8(con, name, val) \
do { \
fputs(name "<- " #val, con); \
} while(0)
#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8)(con, name, val)
int main(void)
{
uint8_t v = 10;
db_put(stdout, "some-field", v);
return 0;
}
When I try compiling this, I get an error:
test.c:15:5: error: use of undeclared identifier 'db_put_u8'
db_put(stdout, "some-field", v);
^
test.c:9:55: note: expanded from macro 'db_put'
#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8)(con, name, val)
^
1 error generated.
Am I doing _Generic
right? The generic selection syntax does not explicitly restrict calling macros in it. Can someone explain what I am doing wrong and how to fix it?
Upvotes: 0
Views: 1050
Reputation: 4217
This line:
#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8)(con, name, val)
First of all has to change to:
#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8(con, name, val))
Because _Generic
works at compile time, not preprocessor time. And after preprocessing, db_put_u8
will not expand because it doesn't exist as a macro. db_put_u8(arg1, arg2, arg3)
, however, does, so you have to put the brackets in the generic. However, there's another error then: _Generic
expects an eression, so a do while
loop would not work. In this case though, you don't even need to encase your macro in a loop because it's just one statement, so it's free from the potential issues of macros with many statements. Your final program should look something like this:
#include <stdio.h>
#include <stdint.h>
#define db_put_u8(con, name, val) \
fputs(name "<- " #val, con)
#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8(con, name, val))
int main(void)
{
uint8_t v = 10;
db_put(stdout, "some-field", v);
return 0;
}
EDIT: In response to the comment, there is a solution. So long as the second thing to do in db_put_u8
is also a statement, you could use the comma operator and a few more brackets. Here's an example program to put it to the test:
#include <stdio.h>
#include <stdint.h>
#define db_put_u8(con, name, val) \
(fputs(name "<- " #val, con), \
fputs("\nAnother test line\n", con))
#define db_put(con, name, val) \
(_Generic(val, uint8_t: db_put_u8(con, name, val)))
int main(void)
{
uint8_t v = 10;
db_put(stdout, "some-field", v);
return 0;
}
When I run this, it outputs:
some-field<- v
Another test line
Upvotes: 3
Reputation: 14137
Macro db_put_u8 would expand as do ... while(...)
statement which is not an expression. _Generic
expects expression thus error is observed. You should provide a function named db_put_u8
. The function name decays to pointer to the function which is an an expression.
Other way around is to replace db_put_u8 with 'fputs(name "<- " #val, con)' inside _Generic and remove final '(name, val,con)'
#define db_put(con, name, val) \
_Generic(val, uint8_t: fputs(name "<- " #val, con))
If db_put_u8
cannot be changed then one could use an C extension named statement expression
. It is supported by GCC and CLANG. Basically it allows to put arbitrary statement info ({ ... })
. The last expression evaluated is returned as result.
#define db_put(con, name, val) \
_Generic(val, uint8_t: ({db_put_u8(con, name, val);}))
Upvotes: 0