Как заставить gcc предупредить о передаче неверного перечисления в функцию - PullRequest
21 голосов
/ 12 января 2011

gcc, похоже, не выдает предупреждение со следующим кодом.Как я могу заставить его выдавать предупреждение?

typedef enum
{
    REG8_A,
    REG8_B,
    REG8_C
}REG8;

typedef enum
{
    REG16_A,
    REG16_B,
    REG16_C
}REG16;

void function(REG8 reg8)
{

}

int main(void)
{
    function(REG16_A);    // Should warn about wrong enum
}

Ответы [ 6 ]

8 голосов
/ 19 января 2014

Чтобы сделать это в C с помощью GCC -Wenum-compare (который включен по умолчанию, если вы включаете -Wall), вы должны выполнить сравнение константы перечисления, прежде чем передать ее в функцию, чтобы получить желаемая диагностика.

-Wenum-сравнить

Предупреждать о сравнении значений различных перечисляемых типов. В C ++ нумерация несовпадений в условных выражениях также диагностируется и предупреждение включен по умолчанию. В C это предупреждение включено -Wall.

http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

Поскольку такое сравнение происходит автоматически, когда мы вызываем функцию, мы можем обернуть функцию в макрос. Для удобства чтения я также определяю макрос SAFE_ENUM, который выполняет безвредное сравнение константы перечисления (это то, что в конечном итоге вызывает предупреждение при попытке передать неверную константу перечисления в foo или bar).

/**
  SAFE_ENUM: evaluate an enumeration constant safely
  TYPE: the enumeration type
  VAL: the enumeration constant to evaluate
*/
#define SAFE_ENUM(TYPE, VAL) ((VAL) == (TYPE)0 ? (VAL) : (VAL))

typedef enum
{
    REG8_DEFAULT,
    REG8_A,
    REG8_B,
    REG8_C
} Reg8;

typedef enum
{
    REG16_DEFAULT,
    REG16_A,
    REG16_B,
    REG16_C
} Reg16;

void foo(Reg8 reg8)
#define foo(reg8) foo(SAFE_ENUM(Reg8, reg8))
{
    printf("%s called with value %d\n", __func__, reg8);
}

void bar(Reg16 reg16)
#define bar(reg16) bar(SAFE_ENUM(Reg16, reg16))
{
    printf("%s called with value %d\n", __func__, reg16);
}

int main(void)
{
    foo(REG8_A);  // ok
    bar(REG16_A); // ok
    foo(REG16_B); // warning
    bar(REG8_B);  // warning

    Reg16 a_reg16 = 42;
    foo(a_reg16); // warning: foo requires a Reg8 but you gave it a Reg16
}
7 голосов
/ 17 января 2011

Причина такого поведения в том, что вы используете компилятор C, а не C ++.И в C типы enum на самом деле не являются типами, перечисления в C просто содержат константы типа int, и их можно свободно смешивать с любыми целыми числами и любой арифметикой.

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

Ваша проблема может быть решена двумя способами:

  • Используйте компилятор C ++.

    Таким образом, у вас будет действительное перечисление , как вы хотите.

  • Измените свой код, чтобы он был встиль pure-C, то есть не использующий перечисления, так как в C это просто наборы констант, где компилятор только помогает вам упорядочивать значения констант.А в C вы будете ответственны за согласованность «типов» переданных констант.Еще раз: для C, члены enum являются просто константами типа int, их нельзя вводить.


#define REG8_A 0
#define REG8_B 1
#define REG8_C 2

#define REG16_A 0
#define REG16_B 1
#define REG16_C 2
6 голосов
/ 12 января 2011

Единственный способ генерировать предупреждение, которое я вижу, - это если вы готовы передавать указатели, а не голые перечисления, например,

typedef enum
{
    REG8_A,
    REG8_B,
    REG8_C
} REG8;

typedef enum
{
    REG16_A,
    REG16_B,
    REG16_C
} REG16;

void function(REG8 * reg8)
{

}

int main(void)
{
    REG16 r = REG16_A;
    function(&r);
    return 0;
}

Не совсем элегантное решение, но оно выдает предупреждение, по крайней мере, с gcc -Wall:

$ gcc -Wall warn_enum.c -o warn_enum
warn_enum.c: In function ‘main’:
warn_enum.c:23: warning: passing argument 1 of ‘function’ from incompatible pointer type
$
2 голосов
/ 18 января 2011

Как уже отмечали другие, C не различает перечислимый тип и базовый целочисленный тип. (Некоторые компиляторы могут включать проверку типов для enum с или typedef с в качестве расширений; YMMV.)

Чтобы получить проверку типов в C, вы можете использовать struct s, но тогда вы потеряете использование встроенных операторов сравнения и возможность switch для переменной. Вы можете попробовать что-то вроде этого, хотя:

typedef struct {
    enum {
        reg8_val_A,
        reg8_val_B,
        reg8_val_C,
    } val;
} reg8;
#define reg8_A (reg8){.val = reg8_val_A}
#define reg8_B (reg8){.val = reg8_val_B}
#define reg8_C (reg8){.val = reg8_val_C}
…
bool
is_A_or_B(reg8 reg) {
    if reg.val == reg8_A.val    // one way to compare
        return true;
    switch (reg.val) {
        case reg8_val_B:        // the other way to compare; note that
            return true;        // “case reg8_B.val:” will *not* work
        case reg8_val_C:
            return false;
        default:
            fprintf(stderr, "bad reg value %d\n", reg.val);
            abort();
    }
}

(Используются некоторые функции C99.)

1 голос
/ 12 марта 2011

С GCC 4.6 вы должны использовать опции -Wconversion и -Werror, чтобы предотвратить любые неявные преобразования типов.Это дает ошибку с кодом, размещенным Полом Р. Но оригинальный код все равно прекрасно компилируется.Я не знаю почему.

0 голосов
/ 17 января 2011
$ g++ test3.cpp -o test3
test3.cpp: In function ‘int main()’:
test3.cpp:22: error: cannot convert ‘REG16’ to ‘REG8’ for argument ‘1’ to ‘void function(REG8)’
...