I want to reduce the chance of mistakes by other developers and I am looking for a solution for limiting the input of macro definition to NOT accept variables as an input.
#define TIMEFRAME_MILISEC(X) ((X))
#define TIMEFRAME_SECONDS(X) ((X) * TIMEFRAME_MILISEC(1000))
#define TIMEFRAME_MINUTES(X) ((X) * TIMEFRAME_SECONDS(60))
#define TIMEFRAME_HOURS(X) ((X) * TIMEFRAME_MINUTES(60))
The usual mistake is that the definition is used for the initialization of variables value and after that calling the definition with the variable again. I just want to be used with "hardcoded" values.
TIMEFRAME_SECONDS(5)
CodePudding user response:
You could create a dirty trick macro such as this:
#define ASSERT_INTCONST(X) ( (int[X]){0} )
- In case
X
is an integer constant expression, this will create a temporary compound literal, which we will discard/optimize out. - In case
X
is a variable expression, there will be a compiler error since compound literals aren't allowed to be of variable length.
Example:
#define ASSERT_INTCONST(X) ( (int[X]){0} )
#define TIMEFRAME_MILISEC(X) ( ASSERT_INTCONST(X), (X) )
#define TIMEFRAME_SECONDS(X) ( ASSERT_INTCONST(X), (X) * TIMEFRAME_MILISEC(1000))
#define TIMEFRAME_MINUTES(X) ( ASSERT_INTCONST(X), (X) * TIMEFRAME_SECONDS(60))
#define TIMEFRAME_HOURS(X) ( ASSERT_INTCONST(X), (X) * TIMEFRAME_MINUTES(60))
int main (void)
{
int x = TIMEFRAME_SECONDS(3); // ok
int y=3;
int z = TIMEFRAME_SECONDS(y); // error: compound literal has variable size
}
For further type safety such as only allowing certain integer types:
#define ASSERT_INTCONST(X) ( (int[X]){ _Generic((X),int:0) } )
Now only integer constant expressions of type int
will get let through. Not long
, unsigned int
, size_t
etc.