10 ответов:
стандарт C11 добавляет
_Static_assertключевое слово._Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */первый слот должен быть неотъемлемой константное выражение. Второй слот-это постоянный строковый литерал, который может быть длинным (
_Static_assert(0, L"assertion of doom!")).Я должен отметить, что это также реализовано в последних версиях лязг.
это работает в функциональной и нефункциональной области (но не внутри структур, союзов).
#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); }
если утверждение времени компиляции не может быть сопоставлено, то почти понятное сообщение генерируется GCC
sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negativeмакрос может или должен быть изменен для создания уникального имени для typedef (т. е. concatenate
__LINE__в концеstatic_assert_...наименование)вместо троичного, это может быть используется также
#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); }UPDATE2: GCC specific code
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
cl
Я знаю, что вопрос явно упоминает gcc, но только для полноты здесь есть настройка для компиляторов Microsoft.
использование отрицательного размера массива typedef не убеждает cl выплюнуть приличную ошибку. Он просто говорит:
error C2118: negative subscript. Битовое поле нулевой ширины в этом отношении лучше. Поскольку это включает в себя typedeffing структуру, нам действительно нужно использовать уникальные имена типов.__LINE__не режет горчицу - можно иметьCOMPILE_TIME_ASSERT()on одна и та же строка в заголовке и исходном файле, и ваша компиляция сломается.__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'
С Википедия:
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;} COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
при использовании макроса STATIC_ASSERT () с
__LINE__, можно избежать столкновения номера строки между записью в a .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]
классический способ - это использование массива:
char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];это работает, потому что если утверждение истинно, массив имеет размер 1 и он действителен, но если он ложен, размер -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; }
объяснение:
Сначала он проверяет, есть ли у вас реальное утверждение, которое вы определенно хотели бы использовать, если это доступный.
Если вы этого не сделаете, он утверждает, получая вашpredicate, и разделив его на себя. Это делает две вещи.
Если это ноль, id est, утверждение не удалось, это вызовет ошибку деления на ноль (арифметика вынуждена, потому что она пытается объявить массив).
Если он не равен нулю, он нормализует размер массива до1. Поэтому, если утверждение прошло, вы все равно не хотите, чтобы оно провалилось, потому что ваш предикат оценивается в-1(недействительным), или232442(массивная трата пространства, IDK, если она будет оптимизирована).
ИбоSTATIC_ASSERTон заключен в фигурные скобки, это делает его блоком, который охватывает переменнуюassert, то есть вы можете написать его много раз.
Он также бросает его вvoid, который является известным способом избавления отunused variableпредупреждения.
ИбоGLOBAL_STATIC_ASSERT, вместо того, чтобы быть в блоке кода, он генерирует пространство имен. Пространства имен разрешены вне функций. Аuniqueидентификатор необходим, чтобы остановить любые конфликтующие определения, если вы используете это несколько раз.
работал для меня на GCC и VS ' 12 C++
это работает, с "удалить неиспользуемые" установить. Я могу использовать одну глобальную функцию для проверки глобальных параметров.
// #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 //
это работало для некоторых старых 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
Я не рекомендуем использовать решение с помощью
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которые оцениваются во время компиляции. Он не будет работать (т. е. компиляция завершится неудачно) с условиями, которые основаны на значениях в памяти, таких как значения, присвоенные переменным.
Comments