Статичне твердження в C


Відповіді:


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!");На моєму комп'ютері я отримую помилку.
emsr

У мене є gcc 4.8.2 на Ubuntu. Пропущена цитата - помилка коментаря (у мене це було в коді). Це перший рядок у файлі після декількох заголовків. Компілятор видає мені дві абсолютно однакові помилки: error: expected declaration specifiers or '...' before 'sizeof'AND 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

92

Це працює у функціональному та нефункціональному обсязі (але не всередині структур, об'єднань).

#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 cpu) компілятора.

ОНОВЛЕННЯ: Для повноти, ось версія з__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); 
}

UPDATE2: Код 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 у вашій відповіді не працює на дзвін.
Елазар,

1
Щодо останнього (специфічного для GCC 4.3 +) рішення: це дуже потужне рішення, оскільки воно може перевірити все, що може зрозуміти оптимізатор, але воно не вдається, якщо оптимізацію не ввімкнено. Однак -Ogдля цього може бути достатньо мінімального рівня оптимізації ( ), проте він не повинен перешкоджати налагодженню. Можна розглянути питання про те, щоб статичне твердження було твердженням про відсутність дії або виконання, якщо __OPTIMIZE____GNUC__) не визначено.
Søren Løvborg

У фрагменті коду з версією LINE (ОНОВЛЕННЯ: Для повноти наведемо версію з `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 ... з випадковими досадами, коли два різні заголовки мають один на одному нумерованому рядку!
М.М.

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
Було б краще, якби ви зробили
Matt Joiner

Це не працює в gcc 4.6 - там сказано "мітка справи не зменшується до цілочислової константи". Це має сенс.
Liosan

Ви обидва, напевно, переїхали до цього часу, але я в підсумку написав свою (див. мою відповідь ). Я використав ваше посилання @MattJoiner, щоб допомогти мені
Hashbrown

І якщо вас можуть турбувати, дайте мені знати, чи це вам підходить, @Liosan. Я тільки почав заглиблюватися в C ++, тому запізнився на вечірку
Hashbrown

Що стосується Visual C ++, він має static_assert вбудований з версії 2010 року, і він працює як в c ++, так і в режимах c. Однак він не має вбудованого c99 _Static_assert.
ddbug

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

1
помилка: "static_assertion_INVALID_CHAR_SIZE" визначено, але не використано [-Werror = unused-variable]
Олексій

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]

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 та пізніших версіях

Наступний простий макрос для 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 -Стіна -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 --це показує, що static_assertіснує "(починаючи з C ++ 11)". Краса моєї відповіді полягає в тому, що він працює у Ccc від gcc та пізніших версій, а також будь-який 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

ах, ох, неправильно прочитав питання. Схоже, я все одно шукав відповіді на 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
//

1
Якщо це взагалі працює, це буде робити лише у джерелі виконуваного файлу.
Кодер

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.