Перейти к содержанию
    

Build-time проверка условий

Вопрос касается не только собственно IAR, но вообще скорее по языку.

Часто сталкиваюсь с необходимостью проверки неких условий внутри макроса на этапе компиляции, при том, что входные аргументы макроса могут быть заданы константами, определёнными не через #define, а через enum.

Пример - имеем некий макрос:

#define m(a) (((a) == 0) ? X: ((a) == 1) ? Y: Z)

Допустиммые значения a для него только: 0, 1, 2.

Хочется сделать внутри макроса, чтобы при подстановке недопустимого значения в качестве аргумента, проект не собирался (компилятор выдавал какую-либо ошибку).

Причём в качестве аргумента могли бы выступать или константы заданные хоть #define-ом хоть enum хоть числовыми константами.

Если бы были допустимы только числовые константы, то проблем нет:

#define CHECK_0 0
#define CHECK_1 1
#define CHECK_2 2
#define concatAB_(a, b) a##b
#define concatAB(a, b) concatAB_(a, b)
#define m(a) ((concatAB(CHECK_, a) == 0) ? X: (concatAB(CHECK_, a) == 1) ? Y: Z)

Для недопустимого аргумента при компиляции будет ошибка "имя CHECK_XXX не определено".

Для констант заданных #define тоже подобное решение. Но вот для выражений и констант заданных enum, этот способ не катит.

 

Пока приходится использовать что-то типа такой конструкции:

#define m(a) (((a) == 0) ? X: ((a) == 1) ? Y: ((a) == 2) ? Z: 1/((a) >= 0 && (a) < 3))

в расчёте на ошибку деления на ноль.

Но может быть есть более элегантное решение? Что-то в голову больше ничего не приходит...... :(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хочется сделать внутри макроса, чтобы при подстановке недопустимого значения в качестве аргумента, проект не собирался (компилятор выдавал какую-либо ошибку).

Это называется assert_static(). Использую его повсеместно. Его делают и через деление на 0, и другие какие-то способы есть. Не важно, как оно сделано. Важно, что используется аналогично традиционному assert().

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

если значение известно на этапе препроцессора, то поможет подобная конструкция

#if ((MACRO1)>5) || ((MACRO1)<1)

#error MACRO1 должен быть от 1 до 4

#endif

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это называется assert_static(). Использую его повсеместно. Его делают и через деление на 0, и другие какие-то способы есть. Не важно, как оно сделано. Важно, что используется аналогично традиционному assert().

В IAR assert_static - undefined :(

А вообще, при значении аргумента x==true, значение assert_static(x) чему должно быть равно (в разных компиляторах и при собственном определении)? Произвольному значению или определённой константе?

 

Определил сейчас так:

#define assert_static(x) ((x) ? 1: 1 / (x))

Использую:

#define A(x) (((x) >> BitrateTab_PACK) * assert_static((x) == (x) >> BitrateTab_PACK << BitrateTab_PACK))
unsigned char const array[] = {A(...), A(...), A(...), ...};

(build-time-проверка, что все члены массива кратны (1 << BitrateTab_PACK)). Но работает конечно, только если при положительном условии assert_static() возвращает ==1.

 

если значение известно на этапе препроцессора, то поможет подобная конструкция

Прочитайте внимательнее о чём был вопрос.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В IAR assert_static - undefined :(

 

В IAR, да и в остальных местах, обычно static_assert(value, "message")

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В IAR assert_static - undefined :(

Ну да. Я обычно добавляю к проекту файл assert_static.h:

#ifndef ASSERT_STATIC_H
#define ASSERT_STATIC_H

#define assert_static(e) do { enum { assert_static__ = 1/(e) }; } while (0)

#endif

А вообще, при значении аргумента x==true, значение assert_static(x) чему должно быть равно (в разных компиляторах и при собственном определении)? Произвольному значению или определённой константе?

Ничему. Как и стандартный assert(), он останавливает компиляцию (обычный - останавливает выполнение программы), если аргумент равен нулю.

Вы придумали нечто иное, но похожее. Логично назвать это как-то по-другому, чтобы другие люди не путались.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ничему. Как и стандартный assert(), он останавливает компиляцию (обычный - останавливает выполнение программы), если аргумент равен нулю.

Вы придумали нечто иное, но похожее. Логично назвать это как-то по-другому, чтобы другие люди не путались.

Мне и нужна остановка компиляции. Только чтобы это можно было встроить внутрь выражения, а не отдельной строкой. Таким образом, чтобы при положительном значении assert, это выражение нормально вычислялось-бы и assert не влиял-бы на него. Чтобы его можно было применять внутри всяческих выражений типа #define name(x) f(...), которые можно подставить как внутрь компилируемых строк, так и внутрь #if.

Пример я приводил.

Тот свой вариант, что я привёл assert_static(), к сожалению в IAR вызывает только варнинг "деление на ноль", а не ошибку. :(

Хотя наверно, опциями проекта, можно данных конкретный варнинг перевести в разряд еррор-ов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Мне и нужна остановка компиляции. Только чтобы это можно было встроить внутрь выражения, а не отдельной строкой. Таким образом, чтобы при положительном значении assert, это выражение нормально вычислялось-бы и assert не влиял-бы на него. Чтобы его можно было применять внутри всяческих выражений типа #define name(x) f(...), которые можно подставить как внутрь компилируемых строк, так и внутрь #if.

 

Тот свой вариант, что я привёл assert_static(), к сожалению в IAR вызывает только варнинг "деление на ноль", а не ошибку. :(

Хотя наверно, опциями проекта, можно данных конкретный варнинг перевести в разряд еррор-ов.

 

надеюсь, что ваш компилятор не поддерживает массивов отрицательной длинны :)

 

#define ASSERT_COMPILE(expr) int __static_assert(int static_assert_failed[(expr)?1:-1])

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может быть оператор запятая поможет? Или конструкция из скобок ({}), хотя это наверно только в gnu.

 

#define assert_static(e)     (1/(e), (e))

Изменено пользователем amaora

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

надеюсь, что ваш компилятор не поддерживает массивов отрицательной длинны :)

#define ASSERT_COMPILE(expr) int __static_assert(int static_assert_failed[(expr)?1:-1])

И как Вы представляете объявление массива внутри выражения????

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И как Вы представляете объявление массива внутри выражения????

Можно попробовать схитрить вот так:

sizeof(union { char c[(e)?-1:1]; })

Не уверен, что стандарт так разрешает, но в GCC работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И как Вы представляете объявление массива внутри выражения????

 

Надо просто прекратить страдать фигней, и жизнь упростится...

 

Для извращенцев:

#define MY_MIN(a,B)  (((a) < (B)) ? (a) : (B)); ASSERT_COMPILE((a) > 2); 

int aa = MY_MIN(1, 17);

Изменено пользователем CrimsonPig

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Надо просто прекратить страдать фигней, и жизнь упростится...

По существу есть что сказать?

 

Для извращенцев:

#define MY_MIN(a,B)  (((a) < (B)) ? (a) : (B)); ASSERT_COMPILE((a) > 2); 
int aa = MY_MIN(1, 17);

Опять мимо кассы. Сколько раз повторять?

 

#define MY_CHECK(x) ... //здесь требуется написать валидный макрос, проверяющий аргумент (x) на соответствие условию нахождения [b]x[/b] в диапазоне [b]Xmin <= x <= Xmax[/b] и вызывающий ошибку (ну хотя-бы варнинг) компиляции в противном случае
static char const m[] = {MY_CHECK(X1+Y1), MY_CHECK(X2+Y2), ...};

Теперь попробуйте вставить сюда, то, что Вы предложили.

 

Можно попробовать схитрить вот так:

sizeof(union { char c[(e)?-1:1]; })

Не уверен, что стандарт так разрешает, но в GCC работает.

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

Ваш вариант в gcc вызывает именно ошибку?

Но всё равно - такой вариант я думаю не получится использовать внутри выражения #if.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но всё равно - такой вариант я думаю не получится использовать внутри выражения #if.

Это к чему вообще? Типа "нет в жизни счастья"? Давайте бросим заниматься этим бренным копанием в компиляторах и станем готовиться к вечной жизни. Может быть, хоть там будет хорошо :wacko:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Ваш вариант в gcc вызывает именно ошибку?

Это как захочешь, у меня именно так.

 

-Werror - Make all warnings into errors.

-Werror= - Make the specified warning into an error.

...

 

 

Также хочу заметить что в стандарте С11 это уже реализовано "из коробки".

тыц

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...