Macro to determine if a struct is a power of 2

T

throwback1986

Hi,

An API requires that buffers be powers of 2, so I borrowed a macro
from Sean Anderson bit hacks page for the following macro:

#define IS_NOT_POWER_OF_2(v) (((v) & ((v) - 1)) && (v))

I then test the condition:

#if IS_NOT_POWER_OF_2(31)
#error "Oops"
#endif

And this works perfectly. However when I try something closer to my
intended use:

#if IS_NOT_POWER_OF_2(sizeof(int))
#error "Oops"
#endif

gcc 3.4.4 reports:
main.cpp:31:34: missing binary operator before token "("

cpp doesn't shed any light, and I have confirmed that my test fails to
compile on other compilers, as well. What am I missing?

Thanks
 
B

Ben Pfaff

throwback1986 said:
#if IS_NOT_POWER_OF_2(sizeof(int))
#error "Oops"
#endif

The C preprocessor doesn't understand "sizeof".

You can put your test in a runtime assertion, or you can do a
build-time assertion in code instead of in preprocessor
directives, e.g.

/* Build-time assertion building block. */
#define BUILD_ASSERT__(EXPR) \
sizeof(struct { unsigned int build_assert_failed : (EXPR) ? 1 : -1; })

/* Build-time assertion for use in a declaration context. */
#define BUILD_ASSERT_DECL(EXPR) \
extern int (*build_assert(void))[BUILD_ASSERT__(EXPR)]

BUILD_ASSERT_DECL(IS_NOT_POWER_OF_2(sizeof(int));
 
E

Eric Sosman

Ben said:
throwback1986 said:
#if IS_NOT_POWER_OF_2(sizeof(int))
#error "Oops"
#endif

The C preprocessor doesn't understand "sizeof".
[...]

To answer the obvious "Why not," observe that the
preprocessor operates at an early stage of compilation,
manipulating the text (actually, the "preprocessing
tokens") of the source. At the time the preprocessor
is making its decisions, not even `int' has yet been
recognized as having any special significance -- it's
just another PP-token, indistinguishable from many others.
Types have not yet been figured out, and the sizes of
types are still in the misty yet-to-be.

The rule is that #if and so on will expand macros in
what they test (the preprocessor obviously knows about
macros), but anything unrecognizable in what's left after
expansion gets replaced by a zero. So your test is

#if IS_NOT_POWER_OF_2(sizeof(int))
-> #if (((sizeof(int)) & ((sizeof(int)) - 1)) && (sizeof(int)))
-> #if (((0(0)) & ((0(0)) - 1)) && (0(0)))

.... (since neither `sizeof' nor `int' has any meaning to
the preprocessor), and the result is not a well-formed
expression.
 
K

Keith Thompson

Ben Pfaff said:
The C preprocessor doesn't understand "sizeof".
[...]

That reminds me of a discussion on comp.std.c back in 1998. The
relevant quotation:

| > You are right. It was nice back in the days when things like
| >
| > #if (sizeof(int) == 8)
| >
| > actually worked (on some compilers).
|
| Must have been before my time.
|
| Dennis

Yes, the poster was Dennis Ritchie.
 
B

Ben Pfaff

Keith Thompson said:
Dennis Ritchie writes:
| Someone writes:
| > You are right. It was nice back in the days when things like
| >
| > #if (sizeof(int) == 8)
| >
| > actually worked (on some compilers).
|
| Must have been before my time.

Yes, the poster was Dennis Ritchie.

I remember that. And I chuckled at the time. But I also
remember that some versions of Turbo C for DOS supported sizeof
in preprocessor directives, so unless my memory is faulty this is
not entirely unprecedented. It's just that "Someone" and Dennis
Ritchie consider "back in the day" to be rather different eras.
 
S

santoshsy

  #if IS_NOT_POWER_OF_2(sizeof(int))
  #error "Oops"
  #endif

#if, #error, #endif, #include ... etc are preprocessor directives.
Preprocessing is the first step of compilation (gcc -E hello.c),
during this stage only preprocessing stuff happens, like macro
expansion, including a library file etc... and preprocessor does not
know what is sizeof() or int, So it'll thow an error.
 
J

James Kuyper

santoshsy said:
#if, #error, #endif, #include ... etc are preprocessor directives.
Preprocessing is the first step of compilation (gcc -E hello.c),
during this stage only preprocessing stuff happens, like macro
expansion, including a library file etc... and preprocessor does not
know what is sizeof() or int, So it'll thow an error.

It doesn't "throw an error" because it doesn't recognize those
identifiers. It objects to that code because it converts those
identifiers into 0, as it's required to do by the standard, and the
expansion of that macro is syntacticly invalid when those identifiers
are replaced by zero. The following preprocessor directive works perfectly:

#if 4*sizeof != int/3
#error "Oops!"
#endif
 
M

mfhaigh

Hi,

An API requires that buffers be powers of 2, so I borrowed a macro
from Sean Anderson bit hacks page for the following macro:

  #define IS_NOT_POWER_OF_2(v) (((v) & ((v) - 1)) && (v))

I then test the condition:

  #if IS_NOT_POWER_OF_2(31)
  #error "Oops"
  #endif

And this works perfectly.  However when I try something closer to my
intended use:

  #if IS_NOT_POWER_OF_2(sizeof(int))
  #error "Oops"
  #endif

gcc 3.4.4 reports:
  main.cpp:31:34: missing binary operator before token "("

cpp doesn't shed any light, and I have confirmed that my test fails to
compile on other compilers, as well. What am I missing?

Try the following code out; this trick works very nicely for what
you're trying to do.

You should note that the value 1 is considered a power of two with the
method you're using, so make sure your API is ok with that.

Mark F. Haigh
(e-mail address removed)


/*
* Cause a compilation error if 'constant_expr' evaluates to zero.
* This construct works because switch statements are not allowed
* to have duplicate case labels. The do { } while(0) wrapper is
* to eat the trailing semicolon.
*
* This particular method of assert was popularized by Jon Jagger.
* No code is emitted when used with modern optimizing compilers.
*/

#define COMPILE_TIME_ASSERT(constant_expr) \
do { \
switch(0) { \
case 0: \
case constant_expr: \
/* nop */ ; \
} \
} while(0)


#define ASSERT_TYPE_SIZE_IS_POWER_OF_2(type) \
COMPILE_TIME_ASSERT(!(sizeof(type) & (sizeof(type) - 1)) \
&& sizeof(type))


int main(void)
{
struct foo {
char a[16];
};

struct bar {
char a[6];
};

ASSERT_TYPE_SIZE_IS_POWER_OF_2(struct foo); /* ok */
ASSERT_TYPE_SIZE_IS_POWER_OF_2(struct bar); /* error */
ASSERT_TYPE_SIZE_IS_POWER_OF_2(int); /* ok */

return 0;
}
 
T

throwback1986

An API requires that buffers be powers of 2, so I borrowed a macro
from Sean Anderson bit hacks page for the following macro:
  #define IS_NOT_POWER_OF_2(v) (((v) & ((v) - 1)) && (v))
I then test the condition:
  #if IS_NOT_POWER_OF_2(31)
  #error "Oops"
  #endif
And this works perfectly.  However when I try something closer to my
intended use:
  #if IS_NOT_POWER_OF_2(sizeof(int))
  #error "Oops"
  #endif
gcc 3.4.4 reports:
  main.cpp:31:34: missing binary operator before token "("
cpp doesn't shed any light, and I have confirmed that my test fails to
compile on other compilers, as well. What am I missing?

Try the following code out; this trick works very nicely for what
you're trying to do.

You should note that the value 1 is considered a power of two with the
method you're using, so make sure your API is ok with that.

Mark F. Haigh
(e-mail address removed)

/*
 * Cause a compilation error if 'constant_expr' evaluates to zero.
 * This construct works because switch statements are not allowed
 * to have duplicate case labels.  The do { } while(0) wrapper is
 * to eat the trailing semicolon.
 *
 * This particular method of assert was popularized by Jon Jagger.
 * No code is emitted when used with modern optimizing compilers.
 */

#define COMPILE_TIME_ASSERT(constant_expr)                      \
    do {                                                        \
        switch(0) {                                             \
            case 0:                                             \
            case constant_expr:                                 \
                /* nop */ ;                                     \
        }                                                       \
    } while(0)

#define ASSERT_TYPE_SIZE_IS_POWER_OF_2(type)                    \
    COMPILE_TIME_ASSERT(!(sizeof(type) & (sizeof(type) - 1))    \
                          && sizeof(type))

int main(void)
{
    struct foo {
        char a[16];
    };

    struct bar {
        char a[6];
    };

    ASSERT_TYPE_SIZE_IS_POWER_OF_2(struct foo);         /* ok */
    ASSERT_TYPE_SIZE_IS_POWER_OF_2(struct bar);         /* error */
    ASSERT_TYPE_SIZE_IS_POWER_OF_2(int);                /* ok */

    return 0;

}

I briefly experimented with the offsetof() macro,as well (until I
realized that cpp doesn't know about it either!) Thanks for all the
comment.
 
N

Nate Eldredge

James Kuyper said:
It doesn't "throw an error" because it doesn't recognize those
identifiers. It objects to that code because it converts those
identifiers into 0, as it's required to do by the standard, and the
expansion of that macro is syntacticly invalid when those identifiers
are replaced by zero. The following preprocessor directive works
perfectly:

#if 4*sizeof != int/3
#error "Oops!"
#endif

That's a weird rule IMHO, and one I didn't know about before. I would
think it would make more sense to "throw an error" (i.e. "require a
diagnostic") if such identifiers remain. The standard behavior has the
undesirable effect of making the following sort of mistake hard to find:

#define LIBRARY_VERSION 14
/* lots of stuff... */
void frob(void) {
#if LIBRAY_VERSION > 10
use_new_version();
#else
use_old_version();
#endif
}

Does anyone know what the point is of silently replacing unrecognized
identifiers with 0? I can't think of a realistic situation where it
would be useful. The Rationale doesn't seem to address it, either. Was
it to maintain compatibility with some historic implementation that did
this?

gcc at least provides the -Wundef option which gives a warning when this
happens.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,237
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top