Статическое утверждение в C

Каков наилучший способ достижения статических утверждений времени компиляции в C (не в C++), с особым акцентом на GCC?

16 ответов

Решение

Стандарт C11 добавляет _Static_assert ключевое слово.

Это реализовано начиная с gcc-4.6:

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

Первый слот должен быть интегральным константным выражением. Второй слот - константный строковый литерал, который может быть длинным (_Static_assert(0, L"assertion of doom!")).

Должен отметить, что это также реализовано в последних версиях Clang.

Это работает в функциональной и нефункциональной области (но не внутри структур, союзов).

#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. Макрос можно или нужно изменить, чтобы сгенерировать уникальное имя для typedef (т.е. сцепить __LINE__ в конце static_assert_... название)

  3. Вместо троичного, это также может быть использовано #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1] который работает даже на ржавом компиляторе olde cc65 (для процессора 6502).

ОБНОВЛЕНИЕ: ради полноты, вот версия с __LINE__

#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); 
}

ОБНОВЛЕНИЕ 2: код GCC

GCC 4.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

Так как:

  1. _Static_assert() теперь определяется в gcc для всех версий C, и
  2. static_assert() определяется в C++ 11 и позже

Следующий простой макрос для STATIC_ASSERT() поэтому работает в:

  1. C++:
    1. C++ 11 (g++ -std=c++11) или позже
  2. 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" 

Примеры:

Протестировано в Ubuntu с использованием gcc 4.8.4:

Пример 1: хорошо gcc выход (то есть: STATIC_ASSERT() коды работают, но условие было ложным, вызывая утверждение во время компиляции):

$ gcc -Wall -o static_assert static_assert.c &&./static_assert
static_assert.c: в функции 'main'
static_assert.c:78:38: ошибка: статическое утверждение не удалось: "(1 > 2) не удалось"
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") fail")
^
static_assert.c: 88: 5: note: в расширении макроса 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Пример 2: хорошо g++ -std=c++11 выход (то есть: STATIC_ASSERT() коды работают, но условие было ложным, вызывая утверждение во время компиляции):

$ g ++ -Wall -std = C++ 11 -o static_assert static_assert.c &&./static_assert
static_assert.c: в функции 'int main()'
static_assert.c:74:32: ошибка: статическое утверждение не удалось: (1 > 2) не удалось
#define _Static_assert static_assert /* static_assert является частью C++ 11 или новее * /
^
static_assert.c:78:38: примечание: в расширении макроса _Static_assert
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") fail")
^
static_assert.c: 88: 5: note: в расширении макроса 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Пример 3: сбой вывода C++ (то есть: код assert вообще не работает должным образом, так как он использует версию C++ до C++ 11):

$ g ++ -Wall -o static_assert static_assert.c &&./static_assert
static_assert.c: 88: 5: предупреждение: идентификатор "static_assert" является ключевым словом в C++11 [-WC++ 0x-compat]
STATIC_ASSERT (1> 2);
^
static_assert.c: в функции 'int main()'
static_assert.c:78:99: ошибка: "static_assert" не был объявлен в этой области
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") fail")
^
static_assert.c: 88: 5: note: в расширении макроса 'STATIC_ASSERT'
STATIC_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://stackru.com/questions/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. https://stackru.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;
}

сл

Я знаю, что этот вопрос явно упоминает gcc, но для полноты здесь есть небольшая настройка компиляторов Microsoft.

Использование массива typedef отрицательного размера не убеждает cl выплевывать приличную ошибку. Это просто говорит error C2118: negative subscript, Битовое поле с нулевой шириной лучше в этом отношении. Поскольку это включает в себя определение типа структуры, нам действительно нужно использовать уникальные имена типов. __LINE__ не режет горчицу - можно иметь COMPILE_TIME_ASSERT() на той же строке в заголовке и исходном файле, и ваша компиляция будет нарушена. __COUNTER__ на помощь приходит (и он был в gcc с 4.3).

#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: "static_assertion_failed_use_another_compiler_luke": именованное битовое поле не может иметь нулевую ширину

Gcc также дает понятное сообщение:

ошибка: нулевая ширина для битового поля "static_assertion_failed_use_another_compiler_luke"

Я НЕ рекомендовал бы использовать решение, используя typedef:

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

Объявление массива с typedef Ключевое слово НЕ гарантируется для оценки во время компиляции. Например, будет скомпилирован следующий код в области блока:

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

Я бы порекомендовал это вместо этого (на C99):

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

Из-за static Ключевое слово, массив будет определен во время компиляции. Обратите внимание, что это утверждение будет работать только с COND которые оцениваются во время компиляции. Он не будет работать с (т. Е. Произойдет сбой компиляции) с условиями, основанными на значениях в памяти, таких как значения, присвоенные переменным.

Из Википедии:

#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );

Классическим способом является использование массива:

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

Это работает, потому что, если утверждение истинно, массив имеет размер 1 и он действителен, но если он ложен, то значение -1 дает ошибку компиляции.

Большинство компиляторов покажут имя переменной и укажут на правую часть кода, где вы можете оставить возможные комментарии об утверждении.

При использовании макроса STATIC_ASSERT() с __LINE__можно избежать столкновения номера строки между записью в файле.c и другой записью в файле заголовка, включив __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]

Общийstatic_assert(C89, C++98 или новее):

Ссылка на Гитхаб

      #define CONCAT_(prefix, suffix)     prefix##suffix
#define CONCAT(prefix, suffix)      CONCAT_(prefix, suffix)


#define outscope_assert(expr)                           \
    struct CONCAT(__outscope_assert_, __COUNTER__)   \
    {                                                   \
        char                                            \
        outscope_assert                                 \
        [2*(expr)-1];                                   \
                                                        \
    } CONCAT(__outscope_assert_, __COUNTER__)

назовите это так:

      outscope_assert( 0 > 5 );

Совместимость с C89:

      gcc main.c -o main.exe -std=c89

или С++98:

      g++ main.c -o main.exe -std=c++98

Ошибка, которую он выдает, выглядит следующим образом:

      main.c:32:9: error: size of array 'outscope_assert' is negative
   32 |         outscope_assert                                 \
      |         ^~~~~~~~~~~~~~~
main.c:50:1: note: in expansion of macro 'outscope_assert'
   50 | outscope_assert(2 > 5);
      | ^~~~~~~~~~~~~~~

Его не так легко сломать, как большинство других предложенных решений, вы можете это проверить!

      /* Some test */

/* Global */
outscope_assert(1 < 2);

/* Within Struct */
struct A
{
    int a;

    outscope_assert(1 < 2);
    outscope_assert(2 > 1); outscope_assert(2 > 1); /* Same Line */
};


int main (void)
{
    /* Within Function */
    outscope_assert(2 > 1);
    return 0;
}

О :

это макрос, который монотонно увеличивает значение во время компиляции (начиная с 0) и обычно используется для генерации идентификаторов.

Макрос может быть определен или не определен в зависимости от компилятора и/или его версии, хотя в основном все современные компиляторы поддерживают его.

Если вы застряли на использовании компилятора, похожего на динозавра или единорога, вам придется использовать альтернативу:

  1. заменить__LINE__, загвоздка в том, что вы больше не сможете размещать статические утверждения в одной строке, и у вас также могут возникнуть проблемы с их размещением в одной строке в разных файлах.
  2. (вероятно, лучший вариант) замените наmy_idи добавьте его в качестве параметра в определение макрофункции следующим образом:#define outscope_assert(expr, myid)а затем вызовите макрос, напримерoutscope_assert(0 < 1 , this_is_myid );
  3. Прочтите следующий абзац, мы будем использовать

(это можно пропустить) Почему?

      #define inscope_assert(expr)                            \
    {                                                   \
        char                                            \
        inscope_assert                                  \
        [2*(expr)-1];                                   \
        (void)inscope_assert;                           \
    }

Как вы могли заметить, это почти то же самое, только безstructиidвещь.

У этого есть очевидные недостатки: его нельзя использовать ни глобально , ни внутри структуры , но с другой стороны, если вы не можете использовать__COUNTER__Вы можете быть уверены, что это будет работать без каких-либо побочных эффектов и гарантирует максимально строгий C89.

Возможно, вы заметили (вероятно, нет) , что если мы используем более строгие флаги при компиляции:

      gcc main.c -o main.exe -std=c89 -Wall -Wextra -ansi -pedantic

нашoutscope_assertзаявлено вmainдайте намwarning:

      warning: unused variable '__outscope_assert__9' [-Wunused-variable]

Просто сказать нам, что мы объявили переменную типа struct, но не использовали ее, на самом деле это не имеет большого значения, но все же нашаinscope_assertне будет производить его.


Специально для C11, C23 и GCC

       C11 standard adds the _Static_assert keyword.
    _Static_assert ( expression , message )
       C23 standard adds the static_assert keyword
    static_assert ( expression , message )
or  static_assert ( expression )
       gcc-4.6 adds the _Static_assert keyword for all versions of C,
including c90, c99, c11, c17, etc.

Итак, просто делаем некоторую предварительную проверку:

      #if !defined static_assert

    /* C11 or later */
    #if defined _Static_assert
        #define static_assert _Static_assert

    /* GCC 4.6 or later */
    #elif defined __GNUC__ && ( __GNUC__ > 4 || __GNUC__ == 4 && defined __GNUC_MINOR__ && __GNUC_MINOR >= 6)
        /*  It will work but it will throw a warning:
            warning: ISO C90 does not support '_Static_assert' [-Wpedantic] 
        */
        #define static_assert _Static_assert
    #endif

#endif

Краткое техническое объяснение:

Развернутый макрос будет выглядеть так:

      struct __outscope_assert_24 { char outscope_assert [2*(2 > 1)-1]; } __outscope_assert_25

Наша цель — выдать ошибку, если выражение (в данном случае2 > 1) неверно:

      error: size of array 'outscope_assert' is negative

Выражение(2 > 1)оценивается как 1 , потому что это правда2*1 - 1 = 1мы объявляем массив размером = 1 .

Выражение(2 > 9)оценивается как 0 , потому что ложь, поэтому2*0 - 1 = -1мы объявляем массивcharразмера = -1 . Это выдает ошибку во время компиляции.

__outscope_assert_24и__outscope_assert_25являются именем структуры и именем переменной, пропуск первого приведет к предупреждению в g++. Пропуск второго приведет к ошибке дублирования члена, если два утверждения объявлены в одной области.

МакросCONCATиCONCAT_здесь для создания имен структур и переменных.

Из Perl, в частности perl.hстрока 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 есть в наличии (от <assert.h>), Это использовано. В противном случае, если условие ложно, объявляется битовое поле с отрицательным размером, что приводит к сбою компиляции.

STMT_START / STMT_END макросы расширяются до do / while (0)соответственно.

Для версий C старше C11 можно создать собственное статическое утверждение. Следующее проверено на старых версиях GCC.

Конечно, если вы можете использовать C11, тогда имеет смысл #include <assert.h> и использовать static_assert.

      /** @file
 * STATIC_ASSERT allows you to do compile time assertions at file scope or in a function.
 * @param expr: a boolean expression that is valid at compile time.
 * @param msg: a "message" that must also be a valid identifier, i.e. message_with_underscores
 */

#ifdef __GNUC__
#define STATIC_ASSERT_HELPER(expr, msg) \
    (!!sizeof(struct { unsigned int STATIC_ASSERTION__##msg: (expr) ? 1 : -1; }))
#define STATIC_ASSERT(expr, msg) \
    extern int (*assert_function__(void)) [STATIC_ASSERT_HELPER(expr, msg)]
#else
    #define STATIC_ASSERT(expr, msg)   \
    extern char STATIC_ASSERTION__##msg[1]; \
    extern char STATIC_ASSERTION__##msg[(expr)?1:2]
#endif /* #ifdef __GNUC__ */

#define STATIC_ASSERT_ARRAY_LEN(array, len) \
    STATIC_ASSERT(sizeof(array)/sizeof(array[0]) == len, array##_wrong_size);

#endif // STATIC_ASSERT_H

Идея по сути такая же, как и в ответе Хэшбрауна, за исключением того, что у меня есть помощник по массиву и специальный случай для gnuc.

Источник: https://github.com/apache/qpid-dispatch/blob/f2e205c733558102006ed6dd0a44453c9821c80a/include/qpid/dispatch/static_assert.h#L23-L44

Просто: включитьsys/cdefs.hи использовать_Static_assert.

Я рекомендую тот же подход для C++ и других компиляторов.

Это сработало для некоторых старых GCC. Извините, что я забыл, что это была за версия:

#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

Это работает с установленной опцией "удалить неиспользуемый". Я могу использовать одну глобальную функцию для проверки глобальных параметров.

//
#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
//

Для тех, кто хочет что-то действительно простое и переносимое, но не имеет доступа к функциям 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;
}

Объяснение:
Сначала он проверяет, есть ли у вас реальное утверждение, которое вы определенно хотели бы использовать, если оно доступно.
Если вы этого не сделаете, то это pred и разделить его на себя. Это делает две вещи.
Если оно равно нулю, то есть утверждение не удалось, это приведет к ошибке деления на ноль (арифметика принудительная, потому что она пытается объявить массив).
Если он не равен нулю, он нормализует размер массива до 1 , Поэтому, если утверждение прошло, вы бы не хотели, чтобы оно все равно не сработало, потому что ваш предикат оценивается как -1 (неверно) или 232442 (огромная трата пространства, IDK, если это будет оптимизировано).
За STATIC_ASSERT он заключен в фигурные скобки, это делает его блоком, который определяет область видимости переменной assert Это означает, что вы можете написать это много раз.
Это также бросает это к void , который является известным способом избавления от unused variable предупреждения.
За GLOBAL_STATIC_ASSERT вместо того, чтобы находиться в блоке кода, он генерирует пространство имен. Пространства имен разрешены вне функций. unique идентификатор необходим, чтобы остановить любые противоречивые определения, если вы используете это более одного раза.


Работал для меня на GCC и VS'12 C++

Удивлен, что об этом не упомянули:

      #define STATIC_ASSERT(condition, message) ((void)sizeof(char[1 - 2*!(condition)]))

У него есть ограничение, заключающееся в том, что вы можете использовать его только в области функции, но у него нет никаких проблем с уникальностью, таких как параметры вне области действия. Также не использует программную память.

Источник: https://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/

Другие вопросы по тегам