Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Build-time проверка условий
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
jcxz
Вопрос касается не только собственно 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))
в расчёте на ошибку деления на ноль.
Но может быть есть более элегантное решение? Что-то в голову больше ничего не приходит...... sad.gif
scifi
Цитата(jcxz @ Mar 2 2016, 11:07) *
Хочется сделать внутри макроса, чтобы при подстановке недопустимого значения в качестве аргумента, проект не собирался (компилятор выдавал какую-либо ошибку).

Это называется assert_static(). Использую его повсеместно. Его делают и через деление на 0, и другие какие-то способы есть. Не важно, как оно сделано. Важно, что используется аналогично традиционному assert().
novikovfb
если значение известно на этапе препроцессора, то поможет подобная конструкция
#if ((MACRO1)>5) || ((MACRO1)<1)
#error MACRO1 должен быть от 1 до 4
#endif
jcxz
Цитата(scifi @ Mar 2 2016, 16:29) *
Это называется assert_static(). Использую его повсеместно. Его делают и через деление на 0, и другие какие-то способы есть. Не важно, как оно сделано. Важно, что используется аналогично традиционному assert().

В IAR assert_static - undefined sad.gif
А вообще, при значении аргумента 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.

Цитата(novikovfb @ Mar 3 2016, 19:11) *
если значение известно на этапе препроцессора, то поможет подобная конструкция

Прочитайте внимательнее о чём был вопрос.
Defin
Цитата(jcxz @ Mar 8 2016, 00:55) *
В IAR assert_static - undefined sad.gif


В IAR, да и в остальных местах, обычно static_assert(value, "message")
scifi
Цитата(jcxz @ Mar 8 2016, 00:55) *
В IAR assert_static - undefined sad.gif

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

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

#endif

Цитата(jcxz @ Mar 8 2016, 00:55) *
А вообще, при значении аргумента x==true, значение assert_static(x) чему должно быть равно (в разных компиляторах и при собственном определении)? Произвольному значению или определённой константе?

Ничему. Как и стандартный assert(), он останавливает компиляцию (обычный - останавливает выполнение программы), если аргумент равен нулю.
Вы придумали нечто иное, но похожее. Логично назвать это как-то по-другому, чтобы другие люди не путались.
jcxz
Цитата(scifi @ Mar 8 2016, 14:12) *
Ничему. Как и стандартный assert(), он останавливает компиляцию (обычный - останавливает выполнение программы), если аргумент равен нулю.
Вы придумали нечто иное, но похожее. Логично назвать это как-то по-другому, чтобы другие люди не путались.

Мне и нужна остановка компиляции. Только чтобы это можно было встроить внутрь выражения, а не отдельной строкой. Таким образом, чтобы при положительном значении assert, это выражение нормально вычислялось-бы и assert не влиял-бы на него. Чтобы его можно было применять внутри всяческих выражений типа #define name(x) f(...), которые можно подставить как внутрь компилируемых строк, так и внутрь #if.
Пример я приводил.
Тот свой вариант, что я привёл assert_static(), к сожалению в IAR вызывает только варнинг "деление на ноль", а не ошибку. sad.gif
Хотя наверно, опциями проекта, можно данных конкретный варнинг перевести в разряд еррор-ов.
CrimsonPig
Цитата(jcxz @ Mar 8 2016, 09:09) *
Мне и нужна остановка компиляции. Только чтобы это можно было встроить внутрь выражения, а не отдельной строкой. Таким образом, чтобы при положительном значении assert, это выражение нормально вычислялось-бы и assert не влиял-бы на него. Чтобы его можно было применять внутри всяческих выражений типа #define name(x) f(...), которые можно подставить как внутрь компилируемых строк, так и внутрь #if.

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


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

#define ASSERT_COMPILE(expr) int __static_assert(int static_assert_failed[(expr)?1:-1])
amaora
Может быть оператор запятая поможет? Или конструкция из скобок ({}), хотя это наверно только в gnu.

Код
#define assert_static(e)     (1/(e), (e))
jcxz
Цитата(CrimsonPig @ Mar 8 2016, 22:52) *
надеюсь, что ваш компилятор не поддерживает массивов отрицательной длинны sm.gif
#define ASSERT_COMPILE(expr) int __static_assert(int static_assert_failed[(expr)?1:-1])

И как Вы представляете объявление массива внутри выражения????
scifi
Цитата(jcxz @ Mar 9 2016, 12:21) *
И как Вы представляете объявление массива внутри выражения????

Можно попробовать схитрить вот так:
Код
sizeof(union { char c[(e)?-1:1]; })

Не уверен, что стандарт так разрешает, но в GCC работает.
CrimsonPig
Цитата(jcxz @ Mar 9 2016, 09:21) *
И как Вы представляете объявление массива внутри выражения????


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

Для извращенцев:
Код
#define MY_MIN(a,B)  (((a) < (B)) ? (a) : (B)); ASSERT_COMPILE((a) > 2);

int aa = MY_MIN(1, 17);
jcxz
Цитата(CrimsonPig @ Mar 9 2016, 16:37) *
Надо просто прекратить страдать фигней, и жизнь упростится...

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

Цитата(CrimsonPig @ Mar 9 2016, 16:37) *
Для извращенцев:
Код
#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), ...};
Теперь попробуйте вставить сюда, то, что Вы предложили.

Цитата(scifi @ Mar 9 2016, 15:35) *
Можно попробовать схитрить вот так:
Код
sizeof(union { char c[(e)?-1:1]; })

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

Да, в принципе можно попробовать. Щас использую вариант с делением на ноль, но деление на ноль вызывает только варнинг компилятора, а не ошибку.
Ваш вариант в gcc вызывает именно ошибку?
Но всё равно - такой вариант я думаю не получится использовать внутри выражения #if.
scifi
Цитата(jcxz @ Mar 10 2016, 07:13) *
Но всё равно - такой вариант я думаю не получится использовать внутри выражения #if.

Это к чему вообще? Типа "нет в жизни счастья"? Давайте бросим заниматься этим бренным копанием в компиляторах и станем готовиться к вечной жизни. Может быть, хоть там будет хорошо wacko.gif
demiurg_spb
Цитата(jcxz @ Mar 10 2016, 07:13) *
Да, в принципе можно попробовать. Щас использую вариант с делением на ноль, но деление на ноль вызывает только варнинг компилятора, а не ошибку.
Ваш вариант в gcc вызывает именно ошибку?

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

-Werror - Make all warnings into errors.
-Werror= - Make the specified warning into an error.
...


Также хочу заметить что в стандарте С11 это уже реализовано "из коробки".
тыц
jcxz
Цитата(demiurg_spb @ Mar 10 2016, 13:38) *
-Werror - Make all warnings into errors.
-Werror= - Make the specified warning into an error.

Да, это я знаю.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.