Как прервать компиляцию на основе «неправильного» значения в переменной
Я ищу код, который может генерировать ошибку времени компиляции, когда переменная содержит неправильное значение.
Однажды я начал с этого кода в программе переменного тока:
#define FIFO_BUFFER_SIZE 8
#define FIFO_BUFFER_MASK ( FIFO_BUFFER_SIZE - 1 )
#if ( FIFO_BUFFER_SIZE & FIFO_BUFFER_MASK )
#error Fifo buffer size is not a power of 2
#endif
Переписал это на C++ для консольного приложения ПК:
MovingAVG::MovingAVG( const unsigned long element_count ) {
ptr = 0;
siz = element_count;
buf = new long[siz];
msk = siz - 1;
if( siz & msk ) {
//std::cerr << "Размер буфера не является степенью числа 2" << станд::эндл;
//выход(-1);
}
}
Теперь я хочу использовать нечто подобное в скетче Arduino. Поскольку терминал не всегда подключен к последовательному порту, а функция exit() в микроконтроллере бесполезна, я ищу способ прервать компиляцию на основе значения переменной. Я не даю себе много шансов найти что-нибудь, но чтобы быть абсолютно уверенным, что это действительно невозможно, я опускаю вопрос здесь.
Я хочу прервать компиляцию (желательно с сообщением), когда параметр конструктора класса содержит недопустимое значение. Это вообще возможно?
На самом деле я хочу увидеть сообщение перед тем, как начать программировать устройство. Ниже я разместил возможное решение, которое выдает сообщение во время связывания, но оно работает только тогда, когда я объявляю класс с постоянным параметром:
class MovingAVG avg( 32 );
@hennep, 👍0
Обсуждение3 ответа
Лучший ответ:
Как подсказывает timemage, я думаю, что static_assert()
является правильным
решение вашей проблемы: это самое ясное и наиболее смысловое
правильный. Проблема в том, что вы не можете подключить static_assert()
напрямую к
ваш код, поскольку выражение, которое вы тестируете, не является временем компиляции
константа.
Я предлагаю вам превратить аргумент конструктора в шаблон аргумент. Это гарантирует, что это константа времени компиляции. В качестве бонуса, вы сможете использовать статически выделенный буфер, что лучше на ОЗУ, чем динамическое выделение:
template <int siz> class MovingAVG {
static const int msk = siz - 1;
long buf[siz];
int ptr = 0;
public:
MovingAVG()
{
static_assert((siz & msk) == 0,
"Element count should be a power of two.");
}
};
Это работает:
MovingAVG<32> filter;
но это не удается с ошибкой: статическое утверждение не удалось: количество элементов должно быть степенью двойки».:
MovingAVG<33> filter;
Ага. Если это дизайнерское решение, которое они могут принять, я бы согласился. Если мы не получаем от них разъяснений, я голосую за это., @timemage
Отличное кодирование. Мне это нравится! Спасибо., @hennep
Поскольку element_count
является параметром метода, он по определению является переменной. Поэтому его значение обычно неизвестно во время компиляции.
Поскольку значение element_count
неизвестно во время компиляции, вы не можете получить ошибку времени компиляции для неправильного значения.
Только если вы используете метод (здесь: конструктор) в той же единице трансляции, что и его определение, и если аргументы всех вызовов известны компилятору (это константы ), компилятор сможет оценить siz & msk
как константу. Обратите внимание, что некоторым компиляторам для этого необходимо получить параметры оптимизации.
Затем вы можете использовать любой подход, который запускает диагностику компилятора, например, такой как этот в C.
Это сработает, очень грязный трюк, но он вызовет ошибку, прежде чем я запрограммирую/проверю устройство. Это может сэкономить несколько программных циклов устройства, но, что более важно, не будет тратить мое время впустую.
extern void ElementCountShouldBeAPowerOfTwo(void);
MovingAVG::MovingAVG( const unsigned long element_count ) {
ptr = 0;
siz = element_count;
buf = new long[siz];
msk = siz - 1;
if( siz & msk ) {
// Эта функция будет удалена оптимизатором
// когда размер буфера является степенью двойки
ElementCountShouldBeAPowerOfTwo();
}
}
Компилятор не будет жаловаться на функцию "ElementCountShouldBeAPowerOfTwo" потому что он помечен как внешний. Компоновщик выдаст ошибку, когда оптимизатор оставит вызов на месте.
Если компоновщик выдает ошибку, он покажет:
undefined reference to `ElementCountShouldBeAPowerOfTwo()'
@Крисл Я использовал код как на C, так и на C++, и я не хочу переписывать часть C++. Вы и @the_busybee оба были правы, когда написали, что ошибка не будет отображаться во время компиляции.
Мне нужно было задать свой вопрос по-другому, я хочу видеть предупреждение перед программированием устройства :-)
Я не помечаю ответ, так как ожидаю, что в будущем при обновлении компилятора это не удастся. На данный момент Arduino 1.8.19 делает то, что мне нужно.
Неважно, как вы его повернете, как и любой другой [метод](https://stackoverflow.com/a/59730203/11294831), это может работать **только**, если значение известно во время компиляции. И это верно только в том случае, если компилятор видит все вызовы (в данном случае конструктора) _и_ их аргументы постоянны. Так что юзабилити действительно очень мало. Было бы неплохо добавить это к вашему вопросу., @the busybee
- C++ против языка Arduino?
- Как использовать SPI на Arduino?
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Ошибка: expected unqualified-id before 'if'
- Что лучше использовать: #define или const int для констант?
- Функции со строковыми параметрами
- Библиотека DHT.h не импортируется
- ошибка: ожидаемое первичное выражение перед токеном ','
Я не уверен, что с этим делать, потому что, вероятно, лучше спросить и ответить на stackoverflow. Почти наверняка так и было. Вы как бы ищете
static_assert
. Однако, чтобы использовать это, у вас на самом деле есть _константное выражение_ (известное вычисление во время компиляции), аsiz & msk
не известно во время компиляции. Таким образом, лучшая часть попытки ответить на этот вопрос, по-видимому, заключается в преобразовании вашего переписывания во что-то, что можно было бы оценить во время компиляции, а это то, что вы, вероятно, должны были бы принимать решения, о которых мы не можем., @timemageВторой код выдает ошибку во время выполнения, а не во время компиляции, поэтому его не нужно запрашивать. Первый фрагмент кода сделает это. По какой именно причине вы не хотите его использовать?, @chrisl