I see below code snippet in fwts code base:
#define FWTS_CONCAT(a, b) a ## b
#define FWTS_CONCAT_EXPAND(a,b) FWTS_CONCAT(a, b)
#define FWTS_ASSERT(e, m) \
enum { FWTS_CONCAT_EXPAND(FWTS_ASSERT_ ## m ## _in_line_, __LINE__) = 1 / !!(e) }
#define FWTS_REGISTER_FEATURES(name, ops, priority, flags, features) \
/* Ensure name is not too long */ \
FWTS_ASSERT(FWTS_ARRAY_LEN(name) < 16, \
fwts_register_name_too_long);
My questions are:
For the definition of
FWTS_ASSERT(e, m), I know the!!can convert whatever value into 1 or 0. But doesn't it cause error forFWTS_ASSERT()when!!(e)evaluates to0thus leads to1/0?And btw, the
FWTS_CONCAT_EXPAND(a,b)andFWTS_CONCAT(a, b)seem to be duplicated, why do we need 2 of them?
ADD 1
Based on @Klas Lindbäck's answer, I want to go through the macro expansion with a concrete example.
Suppose I have:
#define M_1 abc
#define M_2 123
Then I guess the expansion process of FWTS_CONCAT_EXPAND(M_1,M_2) should be:
FWTS_CONCAT_EXPAND(M_1,M_2)
->
FWTS_CONCAT(abc, 123)
->
abc123
If I directly applying FWTS_CONCAT(M_1, M_2), will it be expanded like this?
FWTS_CONCAT(M_1, M_2)
->
M_1M_2
->
Bang! M_1M_2 is an invalid symbol!
(Please correct me if I am wrong...)
ADD 2
Tried with gcc -E macroTest.c -o macroTest.i:
(macroTest.c)
#define M_1 abc
#define M_2 123
#define FWTS_CONCAT(a, b) a ## b
#define FWTS_CONCAT_EXPAND(a,b) FWTS_CONCAT(a, b)
FWTS_CONCAT_EXPAND(M_1, M_2)
FWTS_CONCAT(M_1,M_2)
(macroTest.i)
# 1 "macroTest.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "macroTest.c"
abc123
M_1M_2
I think I get the point of the macro expansion rule. Below are some related concepts and quotation:
Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens. After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded. The result is that the arguments are scanned twice to expand macro calls in them.
When a macro parameter is used with a leading ‘#’, the preprocessor replaces it with the literal text of the actual argument, converted to a string constant.
Token Pasting / Token Concatenation:
It is often useful to merge two tokens into one while expanding macros. This is called token pasting or token concatenation. The ‘##’ preprocessing operator performs token pasting. When a macro is expanded, the two tokens on either side of each ‘##’ operator are combined into a single token, which then replaces the ‘##’ and the two original tokens in the macro expansion.
So the detailed process of my scenario is like this:
FWTS_CONCAT_EXPAND(M_1, M_2)
-> FWTS_CONCAT_EXPAND(abc, 123) // M_1, M_2 pre-expanded since FWTS_CONCAT_EXPAND has no ##.
-> FWTS_CONCAT(abc, 123) // FWTS_CONCAT_EXPAND expanded into FWTS_CONCAT
-> abc123 // FWTS_CONCAT expanded
FWTS_CONCAT(M_1,M_2)
-> M_1M_2 //M_1, M_2 are not pre-expanded because of the ## in FWTS_CONCAT
-> DEADEND
1/0can't be computed.enumtype to raise compile-time error? Anything special aboutenum?enumis nothing special in this; I have usedtypedefarrays with similar trick. Point is that we perform compile time check with C constructs, when preprocessor won't work. For example, you cannot usesizeofin#ifstatement to test array size.