Используйте статический assert для проверки значений константных выражений

      Комментарии к записи Используйте статический assert для проверки значений константных выражений отключены

Главная Форумы Программирование Программирование на С++ Заметки о С++ Замечания о С++ Используйте статический assert для проверки значений константных выражений

Помечено: ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 1 месяц, 1 неделя назад.

  • Автор
    Сообщения
  • #3960

    Многие программисты знакомы с макросом assert(), который проверяет утверждения во время исполнения программы. Однако, он имеет недостатки. Кроме того, что он тратит процессорное время на проверку (что может быть и не существенно) он завершается вызовом abort(), а такая обработка ошибок не подходит для надежных серверных и встраиваемых систем.

    В стандарте C++0x появилась статическая проверка утверждения, которая имеет вид:

    static_assert(constant-expression, string-literal);

    В соответствии с этим стандартом constant-expression должно быть выражением, которое может быть вычислено и приведено к типу bool во время компиляции. Если в результате преобразования выражения получается true, то static_assert не имеет никакого эффекта. В противном случае компилятор выдаст сообщение об ошибке с указанием строки в которой она произошла.

    /* Проходит проверку */
    static_assert(
      sizeof(int) <= sizeof(void*), 
      "sizeof(int) <= sizeof(void*)"
    ); 
     
    /* Сбой */
    static_assert(
      sizeof(double) <= sizeof(int), 
      "sizeof(double) <= sizeof(int)"
    );

    С99 не имеет статического assert, но любой современный компилятор поддерживает эту фичу.

    Пример с ошибкой

    Следующий пример использует утверждение assert() для проверки относительного смещения полей структуры, которое имеет важное значение для корректного исполнения программы. Смещение поля относительно начала структуры можно получить макросом offsetof:

    struct timer {
      uint8_t MODE;
      uint32_t DATA;
      uint32_t COUNT;
    };
     
    int func() { 
      assert(offsetof(timer, DATA) == 4);
    }

    Главным минусом этого подхода является то, что диагностика будет происходить во время исполнения программы и только когда будет вызвана эта функция.

    Правильное решение

    Если в assert() проверяет константное выражение, можно использовать препроцессор, для статической проверки условий:

    struct timer {
      uint8_t MODE;
      uint32_t DATA;
      uint32_t COUNT;
    };
     
    #if (offsetof(timer, DATA) != 4)
      #error "DATA must be at offset 4"
    #endif

    Преимущества на лицо: четкие сообщения об ошибках на стадии компиляции и отсутствие необходимости встраивать вызов функций или макросов еще куда-то в код, что бы они исполнились.

    Но, к сожалению, этот подход не является переносимым. Стандарт C99 не требует реализации поддержки sizeof, offsetof, или перечислимых констант в условиях #if.

    Правильное решение №2

    Следующее решение имитирует static_assert полностью переносимым методом.

    #define JOIN(x, y) JOIN_AGAIN(x, y)
    #define JOIN_AGAIN(x, y) x ## y
     
    #define static_assert(e) \
    typedef char JOIN(assertion_failed_at_line_, __LINE__) [(e) ? 1 : -1]
     
    struct timer {
      uint8_t MODE;
      uint32_t DATA;
      uint32_t COUNT;
    };
     
    static_assert(offsetof(struct timer, DATA == 4);

    Автор статьи — персонаж с именем МУХ.

Для ответа в этой теме необходимо авторизоваться.