Переменная типа C assert - PullRequest
       2

Переменная типа C assert

15 голосов
/ 23 апреля 2019
uint32_t fail_count = 0;

...

if(is_failed)
    if(fail_count < UINT32_MAX - 1 )
        ++fail_count;

Работает нормально, но этот код хрупок.Завтра я могу изменить тип fail_count с uint32_t на int32_t, и я забываю обновить UINT32_MAX.

Есть ли способ заявить, что fail_count это uint32_t нафункция, в которой я написал свои if s?

PS 1- Я знаю, что это легко в C ++, но я ищу способ C.

PS 2- Я предпочитаю использовать два утверждения, чем полагаться на предупреждения компилятора.Проверка размера номера с помощью sizeof должна работать, но есть ли способ отличить тип без знака?

Ответы [ 3 ]

22 голосов
/ 23 апреля 2019

Начиная с C11, вы можете использовать макрос generic selection , чтобы получить результат, основанный на типе выражения.Вы можете использовать результат в статическом утверждении :

#define IS_UINT32(N) _Generic((N), \
  uint32_t: 1, \
  default: 0 \
)

int main(void) {
  uint32_t fail_count = 0;
  _Static_assert(IS_UINT32(fail_count), "wrong type for fail_count");
}

Вы, конечно, можете использовать результат в обычном assert(), но _Static_assert не удастся во время компиляции.

Лучшим подходом может быть отправка сравнения по типу, опять же с использованием общего выбора:

#include <limits.h>
#include <stdint.h>

#define UNDER_LIMIT(N) ((N) < _Generic((N), \
int32_t: INT32_MAX, \
uint32_t: UINT32_MAX \
) -1)

int main(void) {
  int32_t fail_count = 0;

  if (UNDER_LIMIT(fail_count)) {
    ++fail_count;
  }
}
1 голос
/ 23 апреля 2019

А как насчет низкотехнологичного решения, которое работает даже с K & R C и любым компилятором прошлого и настоящего?

Поместите правильный комментарий в нужное место:

/*
 * If this type is changed, don't forget to change the macro in
 * if (fail_count < UINT32_MAX - 1) below (or file foobar.c)
 */
uint32_t fail_count = 0;

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

#define FAIL_COUNT_MAX  UINT32_MAX

рядом с декларацией?В любом случае, это более правильный и чистый код.Не нужно все утверждение магии и ракетостроения: -)

1 голос
/ 23 апреля 2019

Как вы упомянули GCC, вы можете использовать расширение компилятора для этого, если вы не используете C11:

Сначала напишите макрос, который эмулирует C ++ is_same.А затем назовите его с типами, которые вы хотите сравнить.

Минимальный пример для вашего конкретного случая:

#include<assert.h>

#define is_same(a, b) \
  static_assert(__builtin_types_compatible_p(typeof(a), typeof(b)), #a " is not unsigned int")

int main()
{
    int fail_count = 0;    
    is_same(fail_count, unsigned int);
}

Компилятор утверждает:

<source>: In function 'main':
<source>:4:3: error: static assertion failed: "fail_count is not unsigned int"
   static_assert(__builtin_types_compatible_p(typeof(a), typeof(b)), #a " is not unsigned int")
   ^~~~~~~~~~~~~

<source>:9:5: note: in expansion of macro 'is_same'
     is_same(fail_count, unsigned int);
 ^~~~~~~

См. Демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...