Discussion:
Macro to determine if a struct is a power of 2
(too old to reply)
throwback1986
2009-05-14 22:39:48 UTC
Permalink
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
Ben Pfaff
2009-05-14 22:46:40 UTC
Permalink
Post by throwback1986
#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));
--
Ben Pfaff
http://benpfaff.org
Eric Sosman
2009-05-15 01:01:19 UTC
Permalink
Post by Ben Pfaff
Post by throwback1986
#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.
--
Eric Sosman
***@ieee-dot-org.invalid
Keith Thompson
2009-05-15 01:44:37 UTC
Permalink
Post by Ben Pfaff
Post by throwback1986
#if IS_NOT_POWER_OF_2(sizeof(int))
#error "Oops"
#endif
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.
--
Keith Thompson (The_Other_Keith) kst-***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Ben Pfaff
2009-05-15 03:24:58 UTC
Permalink
Post by Keith Thompson
| > 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.
--
char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
=b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
santoshsy
2009-05-15 09:21:36 UTC
Permalink
  #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.


--
Santosh SY.
James Kuyper
2009-05-15 09:56:34 UTC
Permalink
Post by santoshsy
Post by throwback1986
#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.
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
Nate Eldredge
2009-05-15 16:47:21 UTC
Permalink
Post by James Kuyper
Post by santoshsy
Post by throwback1986
#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.
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
#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.

m***@sbcglobal.net
2009-05-15 10:47:03 UTC
Permalink
Post by throwback1986
Hi,
An API requires that buffers be powers of 2, so I borrowed a macro
  #define IS_NOT_POWER_OF_2(v) (((v) & ((v) - 1)) && (v))
  #if IS_NOT_POWER_OF_2(31)
  #error "Oops"
  #endif
And this works perfectly.  However when I try something closer to my
  #if IS_NOT_POWER_OF_2(sizeof(int))
  #error "Oops"
  #endif
  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
***@sbcglobal.net


/*
* 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;
}
throwback1986
2009-05-15 15:20:54 UTC
Permalink
Post by m***@sbcglobal.net
Post by throwback1986
Hi,
An API requires that buffers be powers of 2, so I borrowed a macro
  #define IS_NOT_POWER_OF_2(v) (((v) & ((v) - 1)) && (v))
  #if IS_NOT_POWER_OF_2(31)
  #error "Oops"
  #endif
And this works perfectly.  However when I try something closer to my
  #if IS_NOT_POWER_OF_2(sizeof(int))
  #error "Oops"
  #endif
  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
/*
 * 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.
Continue reading on narkive:
Loading...