How to check if a parameter is an integral constant expression in a C preprocessor macro?

I am currently clearing my existing C library to publish it shamelessly.

The NPOT preprocessor macro is used to compute the next higher power of two for a given integral constant expression at compile time. A macro is commonly used for direct initialization. For all other cases (for example, using variable parameters) there is a built-in function with the same function.

But if the user passes the variable, the algorithm expands to a huge piece of machine code. My question is: What can I do to prevent the user from passing anything other than an integral constant expression into my macro?

 #define NPOT(x) complex_algorithm(x) const int c=10; int main(void) { int i=5; foo = NPOT(5); // works, and does everything it should foo = NPOT(c); // works also, but blows up the code extremely foo = NPOT(i); // blows up the code also } 

What I already tried:

  • Define the macro #define NPOT(x) complex_algorithm(x ## u) . It still works and throws (although hardly useful) a compiler error for variable parameters. If there is no such variable as iu ... Dirty, dangerous, I do not want this.
  • The documentation does not work for most users.
+6
source share
2 answers

You can use any expression that needs a constant integral expression and which will then be optimized.

 #define NPOT(X) \ (1 \ ? complex_algorithm(X) \ : sizeof(struct { int needs_constant[1 ? 1 : (X)]; }) \ ) 

ultimately, you must point the result of sizeof to the appropriate integer type, so the return expression is of the type you expect.

I use unlabeled struct here for

  • are of type, so in fact temporary is not created
  • have a unique type so that the expression can be repeated anywhere in the code without causing conflicts
  • starts using VLA, which is not allowed inside a struct with C99:

An element of a structure or union may have any type of object other than a modified type.

I use triple ?: With 1 as an expression for selection, to ensure that : always evaluated for its type, but never evaluated as an expression.

Edit: It seems that gcc accepts the VLA inside the struct as an extension and doesn't even warn about it, even when I explicitly say -std=c99 . This is a really bad idea about them.

For such a strange compiler :) you can use sizeof((int[X]){ 0 }) instead. This is "forbidden" as stated above, but additionally even gcc complains about it.

+6
source
 #define INTEGRAL_CONST_EXPR(x) ((void) sizeof (struct {int a:(x);}), (x)) 

This will give a compilation error if x not an integral constant expression.

 my_function(INTEGRAL_CONST_EXPR(1 + 2 + 3)); // OK my_function(INTEGRAL_CONST_EXPR(1.0 + 2 + 3)); // compile error 

Note that this solution does not work to initialize a static variable:

 static int a = INTEGRAL_CONST_EXPR(2 + 3); 

causes a compilation error due to expression c , not a constant expression.

As @JensGustedt puts a comment, an integral constant expression resolving a negative integer cannot be used in this solution, since the width of the bit field cannot be negative.

0
source

Source: https://habr.com/ru/post/908410/


All Articles