Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 372 Vote(s) - 3.48 Average
  • 1
  • 2
  • 3
  • 4
  • 5
What is ':-!!' in C?

#1
I bumped into this strange macro code in [/usr/include/linux/kernel.h][1]:

/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))

What does `:-!!` do?

[1]:

[To see links please register here]

Reply

#2
It's creating a size `0` bitfield if the condition is false, but a size `-1` (`-!!1`) bitfield if the condition is true/non-zero. In the former case, there is no error and the struct is initialized with an int member. In the latter case, there is a compile error (and no such thing as a size `-1` bitfield is created, of course).
Reply

#3
The `:` is a bitfield. As for `!!`, that is [logical double negation][1] and so returns `0` for false or `1` for true. And the `-` is a minus sign, i.e. arithmetic negation.

It's all just a trick to get the compiler to barf on invalid inputs.

Consider `BUILD_BUG_ON_ZERO`. When `-!!(e)` evaluates to a negative value, that produces a compile error. Otherwise `-!!(e)` evaluates to 0, and a 0 width bitfield has size of 0. And hence the macro evaluates to a `size_t` with value 0.

The name is weak in my view because the build in fact fails when the input is *not* zero.

`BUILD_BUG_ON_NULL` is very similar, but yields a pointer rather than an `int`.


[1]:

[To see links please register here]

Reply

#4
This is, in effect, **a way to check whether the expression e can be evaluated to be 0, and if not, to fail the build**.

The macro is somewhat misnamed; it should be something more like `BUILD_BUG_OR_ZERO`, rather than `...ON_ZERO`. (There have been **[occasional discussions about whether this is a confusing name][1]**.)

You should read the expression like this:

sizeof(struct { int: -!!(e); }))

1. `(e)`: Compute expression `e`.

2. `!!(e)`: Logically negate twice: `0` if `e == 0`; otherwise `1`.

3. `-!!(e)`: Numerically negate the expression from step 2: `0` if it was `0`; otherwise `-1`.

4. `struct{int: -!!(0);} --> struct{int: 0;}`: If it was zero, then we declare a struct with an anonymous integer bitfield that has width zero. Everything is fine and we proceed as normal.

5. `struct{int: -!!(1);} --> struct{int: -1;}`: On the other hand, if it _isn't_ zero, then it will be some negative number. Declaring any bitfield with _negative_ width is a compilation error.

So we'll either wind up with a bitfield that has width 0 in a struct, which is fine, or a bitfield with negative width, which is a compilation error. Then we take `sizeof` that field, so we get a `size_t` with the appropriate width (which will be zero in the case where `e` is zero).

<hr/>

Some people have asked: **Why not just use an `assert`?**

[keithmo's answer][2] here has a good response:

> These macros implement a compile-time test, while assert() is a run-time test.

Exactly right. You don't want to detect problems in your _kernel_ at runtime that could have been caught earlier! It's a critical piece of the operating system. To whatever extent problems can be detected at compile time, so much the better.

[1]:

[To see links please register here]

[2]:

[To see links please register here]

Reply

#5
Well, I am quite surprised that the alternatives to this syntax have not been mentioned. Another common (but older) mechanism is to call a function that isn't defined and rely on the optimizer to compile-out the function call if your assertion is correct.

#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)

While this mechanism works (as long as optimizations are enabled) it has the downside of not reporting an error until you link, at which time it fails to find the definition for the function you_did_something_bad(). That's why kernel developers starting using tricks like the negative sized bit-field widths and the negative-sized arrays (the later of which stopped breaking builds in GCC 4.4).

In sympathy for the need for compile-time assertions, GCC 4.3 introduced the [`error` function attribute](

[To see links please register here]

) that allows you to extend upon this older concept, but generate a compile-time error with a message of your choosing -- no more cryptic "negative sized array" error messages!

#define MAKE_SURE_THIS_IS_FIVE(number) \
do { \
extern void this_isnt_five(void) __attribute__((error( \
"I asked for five and you gave me " #number))); \
if ((number) != 5) \
this_isnt_five(); \
} while (0)

In fact, as of Linux 3.9, we now have a macro called [`compiletime_assert`](

[To see links please register here]

) which uses this feature and most of the macros in [`bug.h`](

[To see links please register here]

) have been updated accordingly. Still, this macro can't be used as an initializer. However, using by [*statement expressions*](

[To see links please register here]

) (another GCC C-extension), you can!

#define ANY_NUMBER_BUT_FIVE(number) \
({ \
typeof(number) n = (number); \
extern void this_number_is_five(void) __attribute__(( \
error("I told you not to give me a five!"))); \
if (n == 5) \
this_number_is_five(); \
n; \
})

This macro will evaluate its parameter exactly once (in case it has side-effects) and create a compile-time error that says "I told you not to give me a five!" if the expression evaluates to five or is not a compile-time constant.

So why aren't we using this instead of negative-sized bit-fields? Alas, there are currently many restrictions of the use of statement expressions, including their use as constant initializers (for enum constants, bit-field width, etc.) even if the statement expression is completely constant its self (i.e., can be fully evaluated at compile-time and otherwise passes the [`__builtin_constant_p()`](

[To see links please register here]

) test). Further, they cannot be used outside of a function body.

Hopefully, GCC will amend these shortcomings soon and allow constant statement expressions to be used as constant initializers. The challenge here is the language specification defining what is a legal constant expression. C++11 added the constexpr keyword for just this type or thing, but no counterpart exists in C11. While C11 did get static assertions, which will solve part of this problem, it wont solve all of these shortcomings. So I hope that gcc can make a constexpr functionality available as an extension via -std=gnuc99 & -std=gnuc11 or some such and allow its use on statement expressions et. al.
Reply

#6
Some people seem to be confusing these macros with `assert()`.

These macros implement a compile-time test, while `assert()` is a runtime test.

Update:
-
As of C11, the [`_Static_assert()`](

[To see links please register here]

) keyword is available to create compile time tests, and should be used unless code is being written for old compilers.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through