cgccembeddedfirmwareprecompile

NOT accepting variables as an argument in a macro


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)

Solution

  • You could create a dirty trick macro such as this:

    #define ASSERT_INTCONST(X)  ( (int[X]){0} )
    

    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.

    Also if the value zero should be allowed, the macro might have to be (int[(X)+1]) or compiling under strict standard settings may raise complaints about zero-sized arrays.