tags:

views:

153

answers:

3

We have a set of C macros, here, for using the preprocessor to do bitfield operations, and we run into warnings when attempting to use these macros in visual studio. The problem can be demonstrated very easily:

#define BITFIELD_WIDTHMASK(Width) \
    ((Width) >= 32 ? ~0x0ul : (1ul << (Width)) - 1)

unsigned long foo(void)
{
    return BITFIELD_WIDTHMASK(32);
}

Compiling this with MSVC yields the warning:

test.c(12) : warning C4293: '<<' : shift count negative or too big, undefined behavior

This isn't a behavior problem - the << operator won't be used in this case, and that should be detected at compile time. But does anyone have any suggestions about how to rewrite the macro to avoid the warning? Or, failing that, how to redesign the macro interface for this?

Thanks in advance

+3  A: 
#define BITFIELD_WIDTHMASK(Width) (((0x80000000ul >> (32-Width)) << 1) - 1)

Or to handle width of 0 as well as requested...

#define BITFIELD_WIDTHMASK(Width) \
  ((Width) >= 32 \
  ? ~0x0ul \
  : (((1ul << ((Width)/2)) << ((Width)/2)) << ((Width)&1)) - 1)
Doug Currie
It solves the problem stated, but has a similar problem when given a `Width` argument of `0`.
Aidan Cully
#define BITFIELD_WIDTHMASK(Width) ((Width == 0) ? 0 : (Width == 1) ? 1 : (Width == 2) ? 3 : (Width == 3) ? 7 : (Width == 4) ? 15 : ... ;-)
Doug Currie
@Aidan Cully: What result did you want for BITFIELD_WIDTHMASK(0)? because this results in 0xffffff (i.e same as BITFIELD_WIDTHMASK(32)). Yours gave zero. But maybe a bit width of zero serves no purpose.
Clifford
@Clifford: No, the first macro in this answer gives undefined behaviour for width 0, since it tried to shift by 32.
caf
@Clifford - The intention is that `BITFIELD_WIDTHMASK(x)` return a number with the low-order `x` bits set, and the rest cleared. So an argument of `0` should result in `0`, and an argument of `32` should result in `0xffffffff`. A 0-width field serves the purpose of making attempts to write/read that field no-ops, which _might_ be useful for documentation, or for compatibility purposes.
Aidan Cully
Thanks! That did the trick!
Aidan Cully
+1  A: 

The preprocessor not the compiler evaluates the expression when Width is a literal constant, and it is too dumb not to evaluate both sides on the ?: expression. I guess because it does not process it at all but rather inserts an ?: expression with constant operands!

If a width of zero is not required, the following is a simplification that works for 1 to 32:

#define BITFIELD_WIDTHMASK(Width) (~0ul >> (32-(Width)))

It seems to me that if you know the width is zero, (perhaps to disable a feature), which is implicit if you use a literal zero constant, it would be reasonable to just use zero rather than invoke the macro.

Clifford
This seems just as likely to cause the spurious warning as the OP's definition, just on a different set of Width values (the OP's code was correct C, it's just that for some reason the compiler couldn't see that the shift would never be evaluated when Width was too large).
caf
You'll notice that my original solution had a limit like that, but that didn't stop the compiler from warning about the invalid shift. I just tried this version of the macro with the compiler I mentioned, and it generates the same warning.
Aidan Cully
@Aidan Cully: Yes I just realised that, test error on my part: The problem is that the pre=processor still evaluates both right-hand operand expressions in the ?: even when the Width is a constant. I have modified the post (which may render the valid comments obsolete, but just ignores the problem!)
Clifford
+3  A: 

What about:

#define BITFIELD_WIDTHMASK(Width) \
    ((Width) >= 32 ? ~0x0ul : (1ul << (Width % 32)) - 1)

?

caf
Works, and is clean and tight. Exactly what I was after. Thanks!
Aidan Cully
This is as efficient when Width is a constant. If it is a variable however, it may have lower runtime efficiency than a shift if that matters. Still; covers all the basis. +1.
Clifford