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

86

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

Мэтт Джойнер
источник

Ответы:

90

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

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

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

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

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

ЭМСР
источник
4
[... похоже, реализовано gcc, clang ...] Вы можете быть более настойчивыми, если это ;-) _Static_assertявляется частью стандарта C11, и любой компилятор, поддерживающий C11, получит его.
PP,
1
Можно ли это использовать в области файла (вне какой-либо функции)? Потому что я получаю error: expected declaration specifiers or '...' before 'sizeof'строку static_assert( sizeof(int) == sizeof(long int), "Error!); (кстати, я использую C, а не C ++)
user10607
@ user10607 Я удивлен, что это не работает ... Подождите, вам не хватает цитаты в конце строки ошибки. Вставь это и возвращайся. У меня это работает на gcc-4.9: _Static_assert( sizeof(int) == sizeof(long int), "Error!");на моем macine я получаю сообщение об ошибке.
emsr
У меня на Ubuntu стоит gcc 4.8.2. Отсутствующая цитата была опечаткой в ​​комментарии (у меня это было в коде). Это первая строка в файле после нескольких заголовков. Компилятор выдает мне две одинаковые ошибки: error: expected declaration specifiers or '...' before 'sizeof'И error: expected declaration specifiers or '...' before string constant(он имеет в виду "Error!"строку) (также: я компилирую с -std = c11. При помещении объявления внутри функции все работает хорошо (терпит неудачу и завершается успешно, как ожидалось))
user10607
2
@ user10607 Мне также пришлось указать -std = gnu11 в командной строке. Я действительно удивлен, что разница между 4.8 и 4.8 была. У меня есть источник только с одной строкой. Я также использовал стандарт C, а _Static_assertне C ++ static_assert. Вам нужно `#include <assert.h> получить макрос static_assert.
emsr
93

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

#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]что работает даже на ржавом компиляторе old 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 (я полагаю) представил атрибуты функции «ошибка» и «предупреждение». Если вызов функции с этим атрибутом не может быть устранен с помощью удаления мертвого кода (или других мер), генерируется ошибка или предупреждение. Это можно использовать для создания утверждений времени компиляции с определенными пользователем описаниями ошибок. Осталось определить, как их можно использовать в области пространства имен, не прибегая к фиктивной функции:

#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
В Visual Studio просто написано «Отрицательный индекс», не упоминается имя переменной ...
szx
Nordic Mainframe - вариант 3 в вашем ответе не работает на clang.
Элазар
1
Что касается последнего (специфичного для GCC 4.3 +) решения: это очень мощное средство, поскольку оно может проверять все, что может определить оптимизатор, но не работает, если оптимизация не включена. Однако минимального уровня оптимизации ( -Og) часто бывает достаточно для того, чтобы это работало, и он не должен мешать отладке. Можно подумать о том, чтобы статическое утверждение было бездействующим или утверждением времени выполнения, если __OPTIMIZE____GNUC__) не определено.
Søren Løvborg
Во фрагменте кода с версией LINE (UPDATE: для полноты картины здесь версия с LINE) при компиляции выдает ошибки в строке (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), которые можно исправить, добавив еще одну уровень, как показано ниже: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Утверждение в: ## L "");
воскресенье,
Я использую что-то похожее на __LINE__версию в gcc 4.1.1 ... время от времени раздражаю, когда два разных заголовка имеют один в одной пронумерованной строке!
MM
10

cl

Я знаю, что в вопросе явно упоминается 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

бобого
источник
4

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

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

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Тайлер
источник
15
Было бы лучше, если бы вы связались
Мэтт
Это не работает в gcc 4.6 - там написано, что «метка case не сводится к целочисленной константе». В этом есть смысл.
Liosan
вы оба, вероятно, уже перешли, но в итоге я написал свой (см. мой ответ ). Я использовал вашу ссылку @MattJoiner, чтобы помочь мне
Hashbrown
И если вас это беспокоит, дайте мне знать, работает ли это для вас, @Liosan. Я только начал вникать в C ++, поэтому опоздал на вечеринку
Hashbrown
Что касается Visual C ++, он имеет встроенную static_assert с версии 2010 и работает как в режимах C ++, так и в C. Однако он не имеет встроенного c99 _Static_assert.
ddbug 05
3

Я бы НЕ рекомендовал использовать решение, используя 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теми, которые оцениваются во время компиляции. Он не будет работать (т.е. компиляция завершится с ошибкой) с условиями, которые основаны на значениях в памяти, таких как значения, присвоенные переменным.

ФредФредФредФред
источник
4
Хотя это сработает, это также увеличит ваши требования к памяти.
sherrellbc 01
1
ошибка: 'static_assertion_INVALID_CHAR_SIZE' определено, но не используется [-Werror = unused-variable]
Alex
2

При использовании макроса 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]
BrentNZ
источник
1

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

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

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

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

Паоло Болзони
источник
Обертывание этого #define STATIC_ASSERT()макроса универсального типа и предоставление более общих примеров и примеров вывода компилятора из ваших общих примеров STATIC_ASSERT()даст вам гораздо больше голосов и, я думаю, сделает этот метод более понятным.
Габриэль Стейплс
Я не согласен. Компилятор видит макросы мысли и дает более запутанное сообщение.
Паоло Болзони
1

Из 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)соответственно.

мельпомена
источник
1

Так как:

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

Поэтому следующий простой макрос for 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 ") не удалось")
^
static_assert.c: 88: 5: примечание: в раскрытии макроса '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 ") не удалось")
^
static_assert.c: 88: 5: примечание: в раскрытии макроса 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Пример 3: сбой вывода C ++ (то есть: код утверждения не работает должным образом, поскольку он использует версию 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 " ) не удалось ")
^
static_assert.c: 88: 5: примечание: в раскрытии макроса '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. /programming/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. /programming/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. Используйте static_assert для проверки типов, переданных макросу [мой собственный ответ]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Используйте static_assert для проверки типов, переданных в макрос
  3. Как использовать статическое утверждение в C для проверки типов параметров, передаваемых в макрос
Габриэль Скобы
источник
1
Зачем так сложно, когда есть static_assertмакрос assert.h?
Прощай SE
@KamiKaze, я удивлен твоим вопросом, так как кажется, что ты на самом деле не читал мой ответ? Во второй строке моего ответа сказано все: «static_assert () определен в C ++ 11 и более поздних версиях». Следовательно, static_assert()он вообще недоступен в C. См. Также здесь: en.cppreference.com/w/cpp/language/static_assert --it показывает, что static_assertсуществует «(начиная с C ++ 11)». Прелесть моего ответа в том, что он работает в gcc C90 и новее, а также в любом C ++ 11 и новее, а не только в C ++ 11 и новее, например static_assert(). Кроме того, что сложного в моем ответе? Это всего пара #defineс.
Габриэль Стейплс
static_assertопределено в C, поскольку C11. Это макрос, который расширяется до _Static_assert. en.cppreference.com/w/c/error/static_assert . Кроме того, в отличие от вашего ответа, _Static_assertон недоступен в c99 и c90 в gcc (только в gnu99 и gnu90). Это соответствует стандарту. В основном вы делаете много дополнительной работы, которая приносит пользу только в том случае, если она скомпилирована с помощью gnu90 и gnu99, и что делает фактический вариант использования незначительно маленьким.
Прощай SE
> «_Static_assert недоступен в c99 и c90 в gcc (только в gnu99 и gnu90)». Я понимаю, что вы имеете ввиду. Это расширение gcc, так что вы правы. > «В основном вы делаете много лишней работы». Я не согласен; 2 чрезвычайно простых определения - это отнюдь не «много» лишней работы. При этом теперь я понимаю, что вы имеете в виду. Я по-прежнему считаю, что то, что я сделал, полезно и повышает ценность представленных здесь знаний и ответов, поэтому я не думаю, что это заслуживает отрицательного голоса. Кроме того, моя ошибка, когда я сказал «C90 и новее» вместо «gcc C90 и новее» или «g90 и новее», была только в моем комментарии выше, а не в моем ответе.
Габриэль Стейплс
Поскольку это было фактически неверно, голос «против» был оправдан. Если вы исправите неправильные утверждения, я еще раз проверю ответ и могу отозвать свой голос против. Тем не менее, добавление такого кода, если в нем нет необходимости (так что, если вы не работаете с gnu90 и gnu99), не способствует ясности и добавляет больше беспорядка. Если у вас есть вариант использования, это того стоит. Но меня интересует редкость использования, в котором требуется совместимость gnu99 / 90 и c ++ 11.
Прощай SE
0

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

Хэшбраун
источник
2
В C. нет пространств имен
martinkunev 07
ах, упс, неправильно прочитал вопрос. Похоже, я все равно пришел сюда в поисках ответа на C ++ (глядя на последнюю строку моего ответа), поэтому я оставлю его здесь на случай, если другие сделают то же самое
Hashbrown
0

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

//
#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
//
user4978854
источник
1
Если он вообще работает, он будет работать только в исходном коде исполняемого файла.
Coder
0

Это сработало для некоторых старых 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
сойка
источник