Статическое утверждение в C - PullRequest
73 голосов
/ 02 августа 2010

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

Ответы [ 12 ]

0 голосов
/ 05 июня 2015

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

//
#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
//
0 голосов
/ 16 сентября 2014

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

...