问题
I am looking for a way to #define a macro that enforces its arguments to be compile time constants, and at the same time can be used in an expression. The method should be working under C90 and be upward compatible - if possible also portable for the different C++ variants. Also a 0-footprint to memory is preferable.
Consider a compile-time minimum macro as an example. The behavior should be:
#define CT_MIN(CTC_A, CTC B) <<<MAGIC>>>
int a = 1;
int b = a + CT_MIN(1,4); /* OK */
int c = a + CT_MIN(a,4); /* COMPILER DIAGNOSTIC */
To provoke an compile diagnostic, in general I could emulate a static assert with something like:
typedef char ct_check_is_ct_constant[(CTC_A != 0) + 1];
which wouldn't compile (or will raise some diagnostic at least) if anything other than a compile time constant (number) is used for CTC_A; but if CTC_A is an number, it will always be successful (given some care with scopes). But this assert would only work within a multi-statement macro and so would not be possible to use as part of an expression.
I would guess, something in the lines of:
#define CT_MIN(CTC_A, CTC_B) \
( <expr check CTC_A>, \
<expr check CTC_B>, \
(CTC_A) < (CTC_B))? (CTC_A) : (CTC_B) \
)
But I have no idea how the expressions must look like, and if such thing exists.
Any Ideas?
Background:
I have a huge amount of constants from not too much reliable sources via an costumized header. These constants strongly parametrize my code. I want to implement a zero footprint checks during preprocessor and compile time for this constants, to check both my assumptions, the environment, and my skills to write a correct code generator.
回答1:
You can use sizeof
applied to an anonymous struct with a single field whose type is a locally defined enum
whose values must be constant integer expressions:
#define CT_MIN(CTC_A, CTC_B) \
( sizeof(struct { enum { must_be_constant_expression = CTC_A } x; }), \
sizeof(struct { enum { must_be_constant_expression = CTC_B } x; }), \
(CTC_A) < (CTC_B) ? (CTC_A) : (CTC_B) )
The error messages produced by clang is very explicit:
enumassert.c:32:24: error: expression is not an integer constant expression
int c = a + CT_MIN(a,4); /* COMPILER DIAGNOSTIC */
^
enumassert.c:17:60: note: expanded from macro 'CT_MIN'
( sizeof(struct { enum { must_be_constant_expression = CTC_A } x; }), \
^
This solution does not seem to add any symbol to the name space. Furthermore, if you need to handle non integer types, at the cost of a slightly less precise error message, you can add a cast like this:
#define CT_MIN(CTC_A, CTC_B) \
( sizeof(struct { enum { must_be_constant_expression = (int)(CTC_A) } x; }), \
sizeof(struct { enum { must_be_constant_expression = (int)(CTC_B) } x; }), \
(CTC_A) < (CTC_B) ? (CTC_A) : (CTC_B) )
回答2:
For evaluation of both floating point and integer compile-time constants I think I can use @chqrlie's solution and upgrade it a little.
The expression for the check if the parameter is a compile time constant might be:
sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_A))} x; })
(I am not sure if the unary ! gives yield an int
or something of type of CTC_A)
This should handle most of anything, that a MIN operation can be reasonably done for at compile time.
The complete macro then is:
#define CT_MIN(CTC_A, CTC_B) \
( sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_A)) } x; }), \
sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_B)) } x; }), \
(CTC_A) < (CTC_B) ? (CTC_A) : (CTC_B) \
)
来源:https://stackoverflow.com/questions/35107413/macro-for-use-in-expression-while-enforcing-its-arguments-to-be-compile-time-con