代码之家  ›  专栏  ›  技术社区  ›  Matt Joiner

C中的静态断言

  •  71
  • Matt Joiner  · 技术社区  · 14 年前

    11 回复  |  直到 14 年前
        1
  •  97
  •   L29Ah    6 年前

    C11标准增加了 _Static_assert 关键字。

    implemented since gcc-4.6 :

    _Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */
    

    _Static_assert(0, L"assertion of doom!") ).

    我应该注意到,在clang的最新版本中也实现了这一点。

        2
  •  98
  •   ulidtko    7 年前

    这适用于函数和非函数范围(但不适用于结构、联合)。

    #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
    
    STATIC_ASSERT(1,this_should_be_true); 
    
    int main()
    {
     STATIC_ASSERT(1,this_should_be_true); 
    }
    
    1. 如果编译时断言无法匹配,那么GCC将生成一条几乎可理解的消息 sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

    2. __LINE__ 在比赛结束时 static_assert_... (姓名)

    3. 这也可以用来代替三元 #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1] 即使在生锈的旧cc65(对于6502cpu)编译器上也能正常工作。

    更新: 为了完整起见,这是

    #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
    // token pasting madness:
    #define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L)
    #define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L)
    #define COMPILE_TIME_ASSERT(X)    COMPILE_TIME_ASSERT2(X,__LINE__)
    
    COMPILE_TIME_ASSERT(sizeof(long)==8); 
    int main()
    {
        COMPILE_TIME_ASSERT(sizeof(int)==4); 
    }
    

    UPDATE2:GCC特定代码

    GCC4.3(我猜)引入了“error”和“warning”函数属性。如果无法通过死代码消除(或其他措施)消除对具有该属性的函数的调用,则会生成错误或警告。这可用于使用用户定义的故障描述进行编译时断言。仍需确定如何在命名空间范围中使用它们,而无需诉诸伪函数:

    #define CTC(X) ({ extern int __attribute__((error("assertion failure: '" #X "' not true"))) compile_time_check(); ((X)?0:compile_time_check()),0; })
    
    // never to be called.    
    static void my_constraints()
    {
    CTC(sizeof(long)==8); 
    CTC(sizeof(int)==4); 
    }
    
    int main()
    {
    }
    

    $ gcc-mp-4.5 -m32 sas.c 
    sas.c: In function 'myc':
    sas.c:7:1: error: call to 'compile_time_check' declared with attribute error: assertion failure: `sizeof(int)==4` not true
    
        3
  •  11
  •   bobbogo    14 年前

    使用大小为负的数组typedef不能说服 吐出一个像样的错误。只是说 error C2118: negative subscript __LINE__ 不切芥末-有可能有一个 COMPILE_TIME_ASSERT() 在头文件和源文件的同一行上,编译将中断。 __COUNTER__

    #define CTASTR2(pre,post) pre ## post
    #define CTASTR(pre,post) CTASTR2(pre,post)
    #define STATIC_ASSERT(cond,msg) \
        typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \
            CTASTR(static_assertion_failed_,__COUNTER__)
    

    现在

    STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)
    

    在下面 cl 给予:

    错误C2149:“静态\u断言\u失败\u使用另一个\u编译器\u”:指定的位字段不能有零宽度

    Gcc还提供了一个可理解的信息:

        4
  •  4
  •   Tyler    14 年前

    Wikipedia :

    #define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
    
    COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
    
        5
  •  2
  •   Paolo.Bolzoni    9 年前

    char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];
    

    大多数编译器都会显示变量的名称,并指向代码的右侧部分,您可以在那里留下关于断言的最终注释。

        6
  •  2
  •   BrentNZ    7 年前

    如果使用静态\u ASSERT()宏 __LINE__ ,可以通过包括 __INCLUDE_LEVEL__

    例如:

    /* Trickery to create a unique variable name */
    #define BOOST_JOIN( X, Y )      BOOST_DO_JOIN( X, Y )
    #define BOOST_DO_JOIN( X, Y )   BOOST_DO_JOIN2( X, Y )
    #define BOOST_DO_JOIN2( X, Y )  X##Y
    #define STATIC_ASSERT(x)        typedef char \
            BOOST_JOIN( BOOST_JOIN(level_,__INCLUDE_LEVEL__), \
                        BOOST_JOIN(_assert_on_line_,__LINE__) ) [(x) ? 1 : -1]
    
        7
  •  2
  •   FredFredFredFred    6 年前

    不是 typedef :

    #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
    

    类型定义

    int invalid_value = 0;
    STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);
    

    #define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]
    

    因为 static COND 在编译时进行计算。它不适用于基于内存中的值的条件(即编译将失败),例如分配给变量的值。

        8
  •  1
  •   melpomene    5 年前

    尤其是Perl perl.h line 3455 <assert.h> 包括在内):

    /* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
       time invariants. That is, their argument must be a constant expression that
       can be verified by the compiler. This expression can contain anything that's
       known to the compiler, e.g. #define constants, enums, or sizeof (...). If
       the expression evaluates to 0, compilation fails.
       Because they generate no runtime code (i.e.  their use is "free"), they're
       always active, even under non-DEBUGGING builds.
       STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
       file scope (outside of any function).
       STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
       function.
    */
    #if (defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)) && (!defined(__IBMC__) || __IBMC__ >= 1210)
    /* static_assert is a macro defined in <assert.h> in C11 or a compiler
       builtin in C++11.  But IBM XL C V11 does not support _Static_assert, no
       matter what <assert.h> says.
    */
    #  define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
    #else
    /* We use a bit-field instead of an array because gcc accepts
       'typedef char x[n]' where n is not a compile-time constant.
       We want to enforce constantness.
    */
    #  define STATIC_ASSERT_2(COND, SUFFIX) \
        typedef struct { \
            unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
        } _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
    #  define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
    #  define STATIC_ASSERT_DECL(COND)    STATIC_ASSERT_1(COND, __LINE__)
    #endif
    /* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
       error (static_assert is a declaration, and only statements can have labels).
    */
    #define STATIC_ASSERT_STMT(COND)      STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END
    

    如果 static_assert <断言h> ),使用。否则,如果条件为false,则声明大小为负的位字段,这将导致编译失败。

    STMT_START STMT_END 宏正在扩展到 do / while (0) 分别是。

        9
  •  1
  •   Gabriel Staples    4 年前

    1. _Static_assert() 现在在gcc中为C的所有版本定义了,并且
    2. static_assert() 在C++ 11和以后定义

    下面的简单宏 STATIC_ASSERT() 因此在以下方面工作:

      1. C++ 11( g++ -std=c++11
    1. C类:
      1. gcc -std=c90
      2. gcc -std=c99
      3. gcc -std=c11
      4. gcc

    定义 STATIC_ASSERT

    /* For C++: */
    #ifdef __cplusplus
        #ifndef _Static_assert
            #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
        #endif
    #endif
    /* Now for gcc (C) (and C++, given the define above): */
    #define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
    

    现在使用它:

    STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed" 
    

    示例:

    好的 合同通用条款 输出(即: 静态断言() 代码可以工作,但条件为false,导致编译时断言):

    $gcc-Wall-o static\u assert static\u assert.c&&/静态\u断言
    static_assert.c:在函数main中

    #define STATIC_ASSERT(test_for_true)_STATIC_ASSERT((test_for_true),“(”#test_for_true“)failed“)

    static\u assert.c:88:5:注:在宏static\u assert的扩展中

    ^

    好的 输出(即: 静态断言() 代码可以工作,但条件为false,导致编译时断言):



    static\u assert.c:74:32:错误:静态断言失败:(1>2) 失败
    #定义\u Static \u assert Static \u assert/* static_assert
    ^
    static\u assert.c:78:38:注意:在宏的扩展中\u static\u assert

    ^
    static\u assert.c:88:5:注:在宏static\u assert的扩展中

    例3: C++输出(即:断言代码根本不正常工作),因为这是使用C++版本的 之前 C++ 11):

    $g++-Wall-o static\u assert static\u assert.c&&/静态\u断言

    静态断言(1>2);

    static_assert.c:在函数int main()中

    #define STATIC_ASSERT(test_for_true)_STATIC_ASSERT((test_for_true),“(”#test_for_true“)failed“)
    ^
    static\u assert.c:88:5:注:在宏static\u assert的扩展中
    静态断言(1>2);

    完整测试结果如下:

    /*
    static_assert.c
    - test static asserts in C and C++ using gcc compiler
    
    Gabriel Staples
    4 Mar. 2019 
    
    To be posted in:
    1. https://stackoverflow.com/questions/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
    2. https://stackoverflow.com/questions/3385515/static-assert-in-c/7287341#7287341
    
    To compile & run:
      C:
        gcc -Wall -o static_assert static_assert.c && ./static_assert
        gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert
        gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert
        gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert
      C++:
        g++ -Wall -o static_assert static_assert.c && ./static_assert
        g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert
        g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert
        g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert
    
    -------------
    TEST RESULTS:
    -------------
    
    1. `_Static_assert(false, "1. that was false");` works in:
      C:
        gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
        gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
        gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
        gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
      C++:
        g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
        g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
        g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
        g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  NO
    
    2. `static_assert(false, "2. that was false");` works in:
      C:
        gcc -Wall -o static_assert static_assert.c && ./static_assert             NO
        gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    NO
        gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    NO
        gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    NO
      C++:
        g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
        g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
        g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
        g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES
    
    3. `STATIC_ASSERT(1 > 2);` works in:
      C:
        gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
        gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
        gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
        gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
      C++:
        g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
        g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
        g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
        g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES
    
    */
    
    #include <stdio.h>
    #include <stdbool.h>
    
    /* For C++: */
    #ifdef __cplusplus
        #ifndef _Static_assert
            #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
        #endif
    #endif
    /* Now for gcc (C) (and C++, given the define above): */
    #define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
    
    
    int main(void)
    {
        printf("Hello World\n");
    
        /*_Static_assert(false, "1. that was false");*/
        /*static_assert(false, "2. that was false");*/
    
        STATIC_ASSERT(1 > 2);
    
        return 0;
    }
    

    相关:

    1. Use static_assert to check types passed to macro [我自己的答案]
      1. https://en.cppreference.com/w/cpp/types/is_same
      2. https://en.cppreference.com/w/cpp/language/decltype
    2. Use static_assert to check types passed to macro
    3. How to use static assert in C to check the types of parameters passed to a macro
        10
  •  0
  •   Hashbrown    10 年前

    对于那些你想要一些基本的和便携式的,但没有访问C++ 11特性的人,我写了这件事。
    使用 STATIC_ASSERT 通常(如果需要,可以在同一个函数中编写两次)并使用 GLOBAL_STATIC_ASSERT 在函数外,使用唯一短语作为第一个参数。

    #if defined(static_assert)
    #   define STATIC_ASSERT static_assert
    #   define GLOBAL_STATIC_ASSERT(a, b, c) static_assert(b, c)
    #else
    #   define STATIC_ASSERT(pred, explanation); {char assert[1/(pred)];(void)assert;}
    #   define GLOBAL_STATIC_ASSERT(unique, pred, explanation); namespace ASSERTATION {char unique[1/(pred)];}
    #endif
    
    GLOBAL_STATIC_ASSERT(first, 1, "Hi");
    GLOBAL_STATIC_ASSERT(second, 1, "Hi");
    
    int main(int c, char** v) {
        (void)c; (void)v;
        STATIC_ASSERT(1 > 0, "yo");
        STATIC_ASSERT(1 > 0, "yo");
    //    STATIC_ASSERT(1 > 2, "yo"); //would compile until you uncomment this one
        return 0;
    }
    

    说明:
    首先,它检查您是否拥有真正的assert,如果它可用,您肯定希望使用它。
    如果你不这样做,它会通过 pred
    如果它是零,id est,断言失败,它将导致被零除的错误(该算法是强制的,因为它试图声明一个数组)。
    如果不为零,则将数组大小标准化为 1 . 因此,如果断言通过了,您也不会希望它失败,因为谓词的计算结果为 -1 (无效),或 232442 (大量的空间浪费,如果可以优化的话)。
    为了 静态\u断言 它用大括号包装,这使它成为一个块,它限定了变量的范围 assert ,意思是你可以写很多遍。
    它也把它抛给了 void ,这是一种已知的摆脱 unused variable
    全局静态断言 ,而不是在代码块中,它生成一个命名空间。命名空间允许在函数之外。A unique 如果多次使用此定义,则需要标识符来停止任何冲突的定义。


    在GCC和VS 12C++上为我工作

        11
  •  0
  •   user4978854    9 年前

    这在设置了“删除未使用的”选项的情况下起作用。我可以使用一个全局函数来检查全局参数。

    //
    #ifndef __sassert_h__
    #define __sassert_h__
    
    #define _cat(x, y) x##y
    
    #define _sassert(exp, ln) \
    extern void _cat(ASSERT_WARNING_, ln)(void); \
    if(!(exp)) \
    { \
        _cat(ASSERT_WARNING_, ln)(); \
    }
    
    #define sassert(exp) _sassert(exp, __LINE__)
    
    #endif //__sassert_h__
    
    //-----------------------------------------
    static bool tab_req_set_relay(char *p_packet)
    {
        sassert(TXB_TX_PKT_SIZE < 3000000);
        sassert(TXB_TX_PKT_SIZE >= 3000000);
        ...
    }
    
    //-----------------------------------------
    Building target: ntank_app.elf
    Invoking: Cross ARM C Linker
    arm-none-eabi-gcc ...
    ../Sources/host_if/tab_if.c:637: undefined reference to `ASSERT_WARNING_637'
    collect2: error: ld returned 1 exit status
    make: *** [ntank_app.elf] Error 1
    //
    
        12
  •  0
  •   jay    6 年前

    #define _cat(x, y) x##y
    
    #define _sassert(exp, ln)\
    extern char _cat(SASSERT_, ln)[1]; \
    extern char _cat(SASSERT_, ln)[exp ? 1 : 2]
    
    #define sassert(exp) _sassert((exp), __LINE__)
    
    //
    sassert(1 == 2);
    
    //
    #148 declaration is incompatible with "char SASSERT_134[1]" (declared at line 134)  main.c  /test/source/controller line 134    C/C++ Problem