8

I'm currently cleaning up an existing C-library to publish it shamelessly.

A preprocessor macro NPOT is used to calculate the next greater power of two for a given integral constant expression at compile time. The macro is normally used in direct initialisations. For all other cases (e.g. using variable parameters), there is an inline function with the same function.

But if the user passes a variable, the algorithm expands to a huge piece of machine code. My question is: What may I do to prevent a user from passing anything but an integral constant expression to 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:

  1. Define the macro to #define NPOT(x) complex_algorithm(x ## u). It still works and throws a - even if hardly helpful - compiler error for variable parameters. Unless there is no variable like iu... Dirty, dangerous, don't want it.
  2. Documentation, didn't work for most users.
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Lutz
  • 83
  • 5
  • 1
    To moralize, I think this question is a very good example of why macros are bad to begin with. It is sort of like highly-addictive drugs. You know they are bad and once you start with them, you can't get out. You will find yourself needing more and more of them. Just look between the lines of what's really asked for here: "Does anyone know a macro to fix this macro, that calls a macro." – Lundin Feb 14 '12 at 11:00
  • Even so I couldn't find anything bad having a macro calling another macro, that's not the point. Above *complex_algorithm* stands for anything You may want it to.To have a macro in addition to the inline function enables the use of it in direct initialisations while giving the chance to validate the parameters. – Lutz Feb 14 '12 at 11:16

2 Answers2

7

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

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

eventually you should cast the result of the sizeof to the appropriate integer type, so the return expression is of a type that you'd expect.

I am using an untagged struct here to

  • have a type so really no temporary is produced
  • have a unique type such that the expression can be repeated anywhere in the code without causing conflicts
  • trigger the use of a VLA, which is not allowed inside a struct as of C99:

A member of a structure or union may have any object type other than a variably modified type.

I am using the ternary ?: with 1 as the selecting expression to ensure that the : is always evaluated for its type, but never evaluated as an expression.

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

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

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 1
    I think that compiles with C99 VLA's. But `struct { int needs_constant:X; }` should work; variable-length bitfields are still not possible. – MSalters Feb 14 '12 at 09:51
  • @MSalters, no it wouldn't compile with a VLA, they are not allowed inside `struct`. – Jens Gustedt Feb 14 '12 at 09:54
  • VLA's are allowed inside a struct, but only as the last member. – Lutz Feb 14 '12 at 10:11
  • 1
    @Lutz, no the are not allowed. Perhaps you think of flexible array members? These are different animals. Their syntax is with empty `[]` and there is no compile time size information about that flexible part. – Jens Gustedt Feb 14 '12 at 10:13
  • nice use of the conditional operator for the type of the result! – ouah Feb 14 '12 at 10:14
  • While this seems smart at a first glance, the ?: operator is _dangerous_. The result of this macro will be _a promoted type_ of whatever type that holds the most weight. For example, if complex_algorithm returns a `signed short`, the returned type will be an `unsigned int`, because ?: enforces both conditions on each side of `:` to be evaluated as if they were operands of an expression, which in turn results in balancing. See C99 6.5.15/5. A better idea would be to declare the struct first, on a line of its own, surrounded by `{ }`, and then call the complex_algorithm on the next line. – Lundin Feb 14 '12 at 10:32
  • @JensGustedt gcc does not complain, when I call NPOT(i). sizeof(struct { int something[x] } returns 4 times x, so it seems, that the VLA is accepted even inside the struct. – Lutz Feb 14 '12 at 10:47
  • What I tried to say in my previous comment was that the macro must be changed to something like `#define NPOT(X, type) ((type) (1?complex_algorithm(X): ...` – Lundin Feb 14 '12 at 10:52
  • @Lundin, this is what I said from the start, the expression has to be cast to the correct integer type. I didn't know the type that Lutz wanted, so I didn't put anything. – Jens Gustedt Feb 14 '12 at 11:04
  • @Lutz, this is a gcc extension to accept such stuff. (your didn't tag your question with gcc). Clang correctly rejects it. – Jens Gustedt Feb 14 '12 at 11:09
  • @JensGustedt Right, I didn't. The best would be a solution without any compiler dependant behaviour. So if anyone knows an expression that reliably fails for non constant integral expressions on most/all compilers, I would appreciate knowing it. Thank You for your help so far anyway. – Lutz Feb 14 '12 at 11:22
  • @Lutz, it is not clear what that would mean to me. How can we foresee what non standard features compilers can have? The one I gave in my edit *should* work for all C99 compliant compilers, and you could always define a horror macro that combines all such expression that you find into a bigger one by adding them up. – Jens Gustedt Feb 14 '12 at 12:06
  • @JensGustedt I just didn't recognize your modified version yet, so I replied to your next to last answer. The modified version works and is exactly what I was asking for. I didn't mention gcc as I didn't expect the topic to be compiler-specific. Thank You for your help! – Lutz Feb 14 '12 at 13:17
0
#define INTEGRAL_CONST_EXPR(x) ((void) sizeof (struct {int a:(x);}), (x))

This will give a compile error if x is not a 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 for initializing a static variable:

static int a = INTEGRAL_CONST_EXPR(2 + 3);

will trigger a compile error because of an expression with , is not a constant expression.

As @JensGustedt put in the comment, an integral constant expression resolving to a negative integer number cannot be used in this solution as bit-field width cannot be negative.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • No, doesn't work in C99 because then `int[x]` is simply a VLA. Also doesn't work if this is used in a context of a static initialization, since `assert` is a runtime check. – Jens Gustedt Feb 14 '12 at 09:52
  • C99 allows this i think. Check MSalters comment – Anycorn Feb 14 '12 at 09:53
  • @ouah, good try, but still doesn't work :( Bit-fields are even more restricted than array indices, they can't be negative they can't be big. The negative one applies to my solution, too, I'll update that. – Jens Gustedt Feb 14 '12 at 09:59
  • @JensGustedt for VLA and static initialization I thought of it when writing my first attempt, but good point for negative I didn't think o f it. – ouah Feb 14 '12 at 10:01