Reference:
Return type of '?:' (ternary conditional operator): https://stackoverflow.com/questions/8535226/return-type-of-ternary-conditional-operator
__is_constexpr() macro is dark magic: https://lore.kernel.org/linux-hardening/20220131204357.1133674-1-keescook@chromium.org/?fbclid=IwAR0Rgg_tGDk0qiEyuDsuZwERITSdstxmU2-bOtadb7iOOCtw3tgOPKEQ5hE
Using ternary conditional operator to get the type we want for a template type is often used in C++.
Here the same idea applies in C macro:
#define __is_constexpr(x) \
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
Details:
- sizeof() is an integer constant expression, and does not evaluate the
value of its operand; it only examines the type of its operand.
- The results of comparing two integer constant expressions is also
an integer constant expression.
- The use of literal "8" is to avoid warnings about unaligned pointers;
these could otherwise just be "1"s.
- (long)(x) is used to avoid warnings about 64-bit types on 32-bit
architectures.
- The C standard defines an "integer constant expression" as different
from a "null pointer constant" (an integer constant 0 pointer).
- The conditional operator ("... ? ... : ...") returns the type of the
operand that isn't a null pointer constant. This behavior is the
central mechanism of the macro.
- If (x) is an integer constant expression, then the "* 0l" resolves it
into a null pointer constant, which forces the conditional operator
to return the type of the last operand: "(int *)".
- If (x) is not an integer constant expression, then the type of the
conditional operator is from the first operand: "(void *)".
- sizeof(int) == 4 and sizeof(void) == 1.
- The ultimate comparison to "sizeof(int)" chooses between either:
sizeof(*((int *) (8)) == sizeof(int) (x was a constant expression)
sizeof(*((void *)(8)) == sizeof(void) (x was not a constant expression)
For a conditional expression (?:) to be an lvalue, the second and third operands must be lvalues of the same type.
This is because the type and value category of a conditional expression is determined at compile time and must be appropriate whether or not the condition is true.
If one of the operands must be converted to a different type to match the other than the conditional expression cannot be an lvalue as the result of this conversion would not be an lvalue (but a r-value).
Thus:
int x = 1;
int y = 2;
(x > y ? x : y) = 100; // l-value on the left side of =
int x = 1;
long y = 2;
(x > y ? x : y) = 100; // type conversion to long as r-value type
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.