more portable compile-time assert()

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Francois Grieu

    more portable compile-time assert()

    Hi,

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

    #define ASSERT(conditio n) 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(0x1 A5)==0xA5); // check LOW8 works

    int main(void) {return 0;}


    This works on several compilers, but <OT>GCC</OTbarks 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(conditio n) 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
  • Laurent Deniau

    #2
    Re: more portable compile-time assert()

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

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

    STATIC_ASSERT(s izeof_long_is_s maller_than_siz eof_void_ptr,
    sizeof(long) >= sizeof(void*)
    );

    which reports errors like:

    ... invalid enum
    STATIC_ASSERT__ sizeof_long_is_ smaller_than_si zeof_void_ptr

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

    a+, ld.

    Comment

    • Walter Roberson

      #3
      Re: more portable compile-time assert()

      In article <fb744b46-cbad-4ac0-8822-624e180a045f@l3 2g2000hse.googl egroups.com>,
      Francois Grieu <fgrieu@gmail.c omwrote:
      >#define LOW8(x) ((unsigned char)(x))
      >ASSERT(LOW8(0x 1A5)==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 indistinguishab le from a feature."
      -- Rich Kulawiec

      Comment

      • Tomás Ó hÉilidhe

        #4
        Re: more portable compile-time assert()

        Laurent Deniau <Laurent.Deniau @gmail.comwrote 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

        Comment

        • Francois Grieu

          #5
          Re: more portable compile-time assert()

          On Jan 11, 6:09 pm, rober...@ibd.nr c-cnrc.gc.ca (Walter Roberson)
          wrote:
          In article <fb744b46-cbad-4ac0-8822-624e180a0...@l3 2g2000hse.googl egroups.com>,
          Francois Grieu <fgr...@gmail.c omwrote:
          >
          #define LOW8(x) ((unsigned char)(x))
          ASSERT(LOW8(0x1 A5)==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 ?
          <OTmany 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)==0x A5 &&
          LOW8(0)==0 &&
          LOW8(0x80)>=0 &&
          LOW8(-1)>=0 &&
          1==sizeof LOW8(0x12345678 ) &&
          1==sizeof LOW8(1.)
          );

          Comment

          • Malcolm McLean

            #6
            Re: more portable compile-time assert()


            "Tomás Ó hÉilidhe" <toe@lavabit.co mwrote 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.



            Comment

            • Walter Roberson

              #7
              Re: more portable compile-time assert()

              In article <AcudnVmyW7cjVx raRVnyvgA@bt.co m>,
              Malcolm McLean <regniztar@btin ternet.comwrote :
              >"Tomás Ó hÉilidhe" <toe@lavabit.co mwrote 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

              Comment

              • Ark Khasin

                #8
                Re: more portable compile-time assert()

                "Tomï¿½ï¿½ï¿½ï¿ ½ï¿½ï¿½ï¿½ï¿½ï¿ ½ï¿½ï¿½ï¿½ï¿½ï¿ ½ï¿½ï¿½ï¿½ï¿½ï¿ ½ï¿½ï¿½ï¿½ï¿½ï¿ ½ï¿½ï¿½ï¿½ï¿½ï¿ ½ï¿½ï¿½ï¿½" wrote:
                Laurent Deniau <Laurent.Deniau @gmail.comwrote 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(conditio n) \
                extern char dummy_assert_ar ray[(condition)?1:-1]

                and e.g. to make portability issues stand out,
                ASSERT(sizeof(i nt)==sizeof(lon g));

                --
                Ark

                Comment

                • Justin Spahr-Summers

                  #9
                  Re: more portable compile-time assert()

                  On Jan 11, 11:14 am, "Tomás Ó hÉilidhe" <t...@lavabit.c omwrote:
                  Laurent Deniau <Laurent.Den... @gmail.comwrote 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(nam e, cond) \
                  do { \
                  typedef int name ## _assertion_fail ed[(int)(cond) * 2 - 1]; \
                  } while (0)

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

                  Comment

                  • Ark Khasin

                    #10
                    Re: more portable compile-time assert()

                    Justin Spahr-Summers wrote:
                    I recently started using something along these lines (influenced from
                    somewhere, but not sure where off-hand):
                    #define ASSERT_NAME(nam e, cond) \
                    do { \
                    typedef int name ## _assertion_fail ed[(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

                    Comment

                    • Laurent Deniau

                      #11
                      Re: more portable compile-time assert()

                      On 13 jan, 06:34, Ark Khasin <akha...@macroe xpressions.comw rote:
                      Justin Spahr-Summers wrote:
                      I recently started using something along these lines (influenced from
                      somewhere, but not sure where off-hand):
                      #define ASSERT_NAME(nam e, cond) \
                      do { \
                      typedef int name ## _assertion_fail ed[(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(nam e, cond) \
                      typedef int name ## _assertion_fail ed[(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_ar ray[(cond)?1:-1]

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

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

                      ASSERT(sizeof(l ong) >= 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.

                      Comment

                      • Ark Khasin

                        #12
                        Re: more portable compile-time assert()

                        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_ar ray[(cond)?1:-1]
                        >
                        LIB_ASSERT(size of(long) >= sizeof(void*));
                        >
                        #define ASSERT(cond) \
                        extern char dummy_assert_ar ray[(cond)?1:-1]
                        >
                        ASSERT(sizeof(l ong) >= 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_ar ray my_very_own_ass ert_array

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

                        --
                        Ark

                        Comment

                        Working...