more portable compile-time assert()

Discussion in 'C Programming' started by Francois Grieu, Jan 11, 2008.

  1. Hi,

    I'm using an assert()-like macro to test a constant expression at
    compile time

    #define ASSERT(condition) struct{char assert_failure[(condition)?
    1:-1];}

    The idea is that this macro cause a compilation error if a constant
    condition
    is not true (or if the condition is not constant), with some
    (admitedly cryptic)
    error message, e.g. "error: size of array `assert_failure' is
    negative".
    This works including for expression involving sizeof, and casts
    (assuming the compiler is not broken and knows how to perform
    arithmetic
    as the target system does), for examples

    struct { char this, that; } foo;
    ASSERT(2 == sizeof foo); // check size of foo

    #define LOW8(x) ((unsigned char)(x))
    ASSERT(LOW8(0x1A5)==0xA5); // check LOW8 works

    int main(void) {return 0;}


    This works on several compilers, but <OT>GCC</OT> barks with a warning
    on the tune of "unnamed struct/union that defines no instances".

    I'm looking for a more portable alternative. Came up with

    #define ASSER1(x) assert_failure_##x
    #define ASSER2(x) ASSER1(x)
    #define ASSERT(condition) struct ASSER2(__LINE__){char
    assert_failure[(condition)?1:-1];}

    which mostly works, except if ASSERT is used twice on the same line,
    or worse, twice at lines with the same number in different files (this
    can
    occur with headers).

    Anything more robust, and less heavy on namespace polution ?

    TIA,

    Francois Grieu
    Francois Grieu, Jan 11, 2008
    #1
    1. Advertising

  2. Francois Grieu a écrit :
    > Hi,
    >
    > I'm using an assert()-like macro to test a constant expression at
    > compile time
    >
    > #define ASSERT(condition) struct{char assert_failure[(condition)?
    > 1:-1];}


    I am using something close to:

    #define STATIC_ASSERT(tag,cond) \
    enum { STATIC_ASSERT__ ## tag = 1/(cond) }

    STATIC_ASSERT(sizeof_long_is_smaller_than_sizeof_void_ptr,
    sizeof(long) >= sizeof(void*)
    );

    which reports errors like:

    ... invalid enum
    STATIC_ASSERT__sizeof_long_is_smaller_than_sizeof_void_ptr

    the enum ensures compile-time assert and avoid the problem with
    runtime sizeof.

    a+, ld.
    Laurent Deniau, Jan 11, 2008
    #2
    1. Advertising

  3. In article <>,
    Francois Grieu <> wrote:

    >#define LOW8(x) ((unsigned char)(x))
    >ASSERT(LOW8(0x1A5)==0xA5); // check LOW8 works


    That assumes that unsigned char has exactly 8 bits, which is not
    a good assumption. If you want the low 8 bits, why not use
    bitwise-and ?

    I also question whether you really want LOW8 to have a different
    type than x? It is not obvious that sizeof LOW8(x) should be
    different than sizeof x .

    Your definition of LOW8 also does not work if x is a floating
    point type, and is not certain to do anything useful if x is
    a pointer.
    --
    "Any sufficiently advanced bug is indistinguishable from a feature."
    -- Rich Kulawiec
    Walter Roberson, Jan 11, 2008
    #3
  4. Francois Grieu

    Ben Pfaff Guest

    Ben Pfaff, Jan 11, 2008
    #4
  5. Laurent Deniau <> wrote in comp.lang.c:


    > I am using something close to:



    Could other people please post the compile-time asserts they're using (aka
    "static asserts").

    I'd like to compare them and pick the best one to use in my own code.

    --
    Tomás Ó hÉilidhe
    Tomás Ó hÉilidhe, Jan 11, 2008
    #5
  6. On Jan 11, 6:09 pm, -cnrc.gc.ca (Walter Roberson)
    wrote:
    > In article <>,
    > Francois Grieu <> wrote:
    >
    > >#define LOW8(x) ((unsigned char)(x))
    > >ASSERT(LOW8(0x1A5)==0xA5); // check LOW8 works

    >
    > That assumes that unsigned char has exactly 8 bits, which is not
    > a good assumption. If you want the low 8 bits, why not use
    > bitwise-and ?


    <OT> many compilers generate much better code for a cast to unsigned
    char than for a bitwise AND. </OT>

    An alternative might be

    #include <limits.h>

    // LOW8(x) efficiently casts x to unsigned char and
    // keeps only the low 8 bits; result is an unsigned char
    #if UCHAR_MAX==0xFF
    #define LOW8(x) ((unsigned char)(x))
    #else
    #define LOW8(x) ((unsigned char)((unsigned char)(x)&0xFF))
    #endif
    ASSERT( // check LOW8 works
    LOW8(0x1A5)==0xA5 &&
    LOW8(0)==0 &&
    LOW8(0x80)>=0 &&
    LOW8(-1)>=0 &&
    1==sizeof LOW8(0x12345678) &&
    1==sizeof LOW8(1.)
    );
    Francois Grieu, Jan 11, 2008
    #6
  7. "Tomás Ó hÉilidhe" <> wrote in message
    >
    > Could other people please post the compile-time asserts they're using (aka
    > "static asserts").
    >

    This is basically your method for picking up compile time faults.

    #if condition
    #error "condition was true"
    #endif

    There are subtle problems with it because the #if condition is expanded by
    the preprocessor, not the compiler itself, but it is the standard facility
    provided and you should use it.

    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
    Malcolm McLean, Jan 11, 2008
    #7
  8. In article <>,
    Malcolm McLean <> wrote:

    >"Tomás Ó hÉilidhe" <> wrote in message


    >> Could other people please post the compile-time asserts they're using (aka
    >> "static asserts").


    >This is basically your method for picking up compile time faults.


    >#if condition
    >#error "condition was true"
    >#endif


    >There are subtle problems with it because the #if condition is expanded by
    >the preprocessor, not the compiler itself, but it is the standard facility
    >provided and you should use it.


    Unfortunately the preprocessor will not evaluate sizeof()
    (or at least not and get the sizes that would be generated at compile time!)
    --
    "I will speculate that [...] applications [...] could actually see a
    performance boost for most users by going dual-core [...] because it
    is running the adware and spyware that [...] are otherwise slowing
    down the single CPU that user has today" -- Herb Sutter
    Walter Roberson, Jan 11, 2008
    #8
  9. Francois Grieu

    Ark Khasin Guest

    "Tom��������������������������������" wrote:
    > Laurent Deniau <> wrote in comp.lang.c:
    >
    >
    >> I am using something close to:

    >
    >
    > Could other people please post the compile-time asserts they're using
    > (aka "static asserts").
    >
    > I'd like to compare them and pick the best one to use in my own code.
    >

    It's been discussed here some time ago; here it is again:
    #define ASSERT(condition) \
    extern char dummy_assert_array[(condition)?1:-1]

    and e.g. to make portability issues stand out,
    ASSERT(sizeof(int)==sizeof(long));

    --
    Ark
    Ark Khasin, Jan 12, 2008
    #9
  10. On Jan 11, 11:14 am, "Tomás Ó hÉilidhe" <> wrote:
    > Laurent Deniau <> wrote in comp.lang.c:
    >
    > > I am using something close to:

    >
    > Could other people please post the compile-time asserts they're using (aka
    > "static asserts").
    >
    > I'd like to compare them and pick the best one to use in my own code.


    I recently started using something along these lines (influenced from
    somewhere, but not sure where off-hand):
    #define ASSERT_NAME(name, cond) \
    do { \
    typedef int name ## _assertion_failed[(int)(cond) * 2 - 1]; \
    } while (0)

    I personally like using a "name" argument to the macro to avoid
    collisions when using __LINE__.
    Justin Spahr-Summers, Jan 13, 2008
    #10
  11. Francois Grieu

    Ark Khasin Guest

    Justin Spahr-Summers wrote:
    > I recently started using something along these lines (influenced from
    > somewhere, but not sure where off-hand):
    > #define ASSERT_NAME(name, cond) \
    > do { \
    > typedef int name ## _assertion_failed[(int)(cond) * 2 - 1]; \
    > } while (0)
    >
    > I personally like using a "name" argument to the macro to avoid
    > collisions when using __LINE__.

    Suggestions:
    1 - Remove spurious "do{" and ";}while(0)". Then you'd be able to use
    ASSERT_NAME outside of any block, where "compile-time asserts" ought to be.
    2 - There is still a small chance of name collision; to avoid it safely
    and forever, replace "typedef" with "extern"
    3 - If #2 is done, you no longer need the "name" argument, which fact
    simplifies the matters.
    Hmmm... After all these are done, what remains is effectively the same
    as I posted elsethread.
    --
    Ark
    Ark Khasin, Jan 13, 2008
    #11
  12. On 13 jan, 06:34, Ark Khasin <> wrote:
    > Justin Spahr-Summers wrote:
    > > I recently started using something along these lines (influenced from
    > > somewhere, but not sure where off-hand):
    > > #define ASSERT_NAME(name, cond) \
    > > do { \
    > > typedef int name ## _assertion_failed[(int)(cond) * 2 - 1]; \
    > > } while (0)

    >
    > > I personally like using a "name" argument to the macro to avoid
    > > collisions when using __LINE__.

    >
    > Suggestions:
    > 1 - Remove spurious "do{" and ";}while(0)". Then you'd be able to use
    > ASSERT_NAME outside of any block, where "compile-time asserts" ought to be.
    > 2 - There is still a small chance of name collision; to avoid it safely
    > and forever, replace "typedef" with "extern"


    more importantly than name collision, typedef doesn't garantee a
    compile-time error:

    #define ASSERT_NAME(name, cond) \
    typedef int name ## _assertion_failed[(cond)?1:-1]

    void f(int n)
    {
    ASSERT(n > 0); // compile
    }

    > 3 - If #2 is done, you no longer need the "name" argument, which fact
    > simplifies the matters.


    providing a tag or a name should trigger some better error message.
    And still name collision may happen with extern:

    #define LIB_ASSERT(cond) \
    extern int dummy_assert_array[(cond)?1:-1]

    LIB_ASSERT(sizeof(long) >= sizeof(void*));

    #define ASSERT(cond) \
    extern char dummy_assert_array[(cond)?1:-1]

    ASSERT(sizeof(long) >= sizeof(void*));

    while this has little chance to happen, it must still be considered in
    case of a third party lib use the same trick and the same name but
    with a different type.

    > Hmmm... After all these are done, what remains is effectively the same
    > as I posted elsethread.


    a+, ld.
    Laurent Deniau, Jan 14, 2008
    #12
  13. Francois Grieu

    Ark Khasin Guest

    Laurent Deniau wrote:
    >> 3 - If #2 is done, you no longer need the "name" argument, which fact
    >> simplifies the matters.

    >
    > providing a tag or a name should trigger some better error message.
    > And still name collision may happen with extern:
    >
    > #define LIB_ASSERT(cond) \
    > extern int dummy_assert_array[(cond)?1:-1]
    >
    > LIB_ASSERT(sizeof(long) >= sizeof(void*));
    >
    > #define ASSERT(cond) \
    > extern char dummy_assert_array[(cond)?1:-1]
    >
    > ASSERT(sizeof(long) >= sizeof(void*));
    >
    > while this has little chance to happen, it must still be considered in
    > case of a third party lib use the same trick and the same name but
    > with a different type.
    >

    IMHO, third party lib should not define any of this stuff in its public
    header(s).

    Anyway, that's simple to deal with:
    #define dummy_assert_array my_very_own_assert_array

    If you still have a collision with somebody else's names, change the macro.

    --
    Ark
    Ark Khasin, Jan 17, 2008
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ra
    Replies:
    5
    Views:
    1,975
    cristian
    Sep 30, 2004
  2. Robert Brewer
    Replies:
    1
    Views:
    480
    bsmith
    Nov 7, 2004
  3. Thomas Guettler

    assert 0, "foo" vs. assert(0, "foo")

    Thomas Guettler, Feb 23, 2005, in forum: Python
    Replies:
    3
    Views:
    2,501
    Carl Banks
    Feb 23, 2005
  4. Alex Vinokur

    assert(x) and '#define ASSERT(x) assert(x)'

    Alex Vinokur, Nov 25, 2004, in forum: C Programming
    Replies:
    5
    Views:
    896
    Keith Thompson
    Nov 25, 2004
  5. ImpalerCore

    To assert or not to assert...

    ImpalerCore, Apr 27, 2010, in forum: C Programming
    Replies:
    79
    Views:
    1,636
    Richard Bos
    May 17, 2010
Loading...

Share This Page