Как прервать компиляцию на основе «неправильного» значения в переменной

c++

Я ищу код, который может генерировать ошибку времени компиляции, когда переменная содержит неправильное значение.

Однажды я начал с этого кода в программе переменного тока:

#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 );

, 👍0

Обсуждение

Я не уверен, что с этим делать, потому что, вероятно, лучше спросить и ответить на stackoverflow. Почти наверняка так и было. Вы как бы ищете static_assert. Однако, чтобы использовать это, у вас на самом деле есть _константное выражение_ (известное вычисление во время компиляции), а siz & msk не известно во время компиляции. Таким образом, лучшая часть попытки ответить на этот вопрос, по-видимому, заключается в преобразовании вашего переписывания во что-то, что можно было бы оценить во время компиляции, а это то, что вы, вероятно, должны были бы принимать решения, о которых мы не можем., @timemage

Второй код выдает ошибку во время выполнения, а не во время компиляции, поэтому его не нужно запрашивать. Первый фрагмент кода сделает это. По какой именно причине вы не хотите его использовать?, @chrisl


3 ответа


Лучший ответ:

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


1

Поскольку element_count является параметром метода, он по определению является переменной. Поэтому его значение обычно неизвестно во время компиляции.

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


Только если вы используете метод (здесь: конструктор) в той же единице трансляции, что и его определение, и если аргументы всех вызовов известны компилятору (это константы ), компилятор сможет оценить siz & msk как константу. Обратите внимание, что некоторым компиляторам для этого необходимо получить параметры оптимизации.

Затем вы можете использовать любой подход, который запускает диагностику компилятора, например, такой как этот в C.

,

1

Это сработает, очень грязный трюк, но он вызовет ошибку, прежде чем я запрограммирую/проверю устройство. Это может сэкономить несколько программных циклов устройства, но, что более важно, не будет тратить мое время впустую.

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