Принудительная ошибка компиляции, если аргумент функции находится вне диапазона

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

Есть ли способ получить ошибки компиляции в закомментированных строках? Если это помогает, значения enum не могут перекрываться.


enum hundred {
    VALUE_HUNDRED_A = 100,
    VALUE_HUNDRED_B
};

enum thousand {
    VALUE_THOUSAND_A = 1000,
    VALUE_THOUSAND_B
};

void print_hundred(enum hundred foo)
{
    switch (foo) {
        case VALUE_HUNDRED_A:     printf("hundred:a\n");     break;
        case VALUE_HUNDRED_B:     printf("hundred:b\n");     break;
        default: printf("hundred:error(%d)\n", foo); break;
    }
}

void print_thousand(enum thousand bar)
{
    switch (bar) {
        case VALUE_THOUSAND_A:     printf("thousand:a\n");     break;
        case VALUE_THOUSAND_B:     printf("thousand:b\n");     break;
        default: printf("thousand:error(%d)\n", bar); break;
    }
}

int main(void)
{
    print_hundred(VALUE_HUNDRED_A);
    print_hundred(VALUE_THOUSAND_A);  /* Want a compile error here */

    print_thousand(VALUE_THOUSAND_A);
    print_thousand(VALUE_HUNDRED_A);  /* Want a compile error here */

    return 0;
}
3 c
13.12.2008 03:21:37
6 ОТВЕТОВ

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

Я предполагаю, что большинство компиляторов C будут переводить оба ваших перечисления в простые целые числа или шорты или что-то еще, и не будут делать ничего кроме этого.

Так что, насколько я могу судить, ответ будет знать.

-1
13.12.2008 03:27:31

Нет никакого способа, которым C мог бы это сделать, потому что компилятор не знает ничего, кроме базовых типов. Обычно используется assert()макрос, но это проверка во время выполнения.

-1
13.12.2008 03:36:32

Вы не можете сделать это. В C ++ вы можете перегружать функцию и делать некоторые хитрости (или использовать boost :: enable_if), или просто полагаться на безопасность типов в C ++, делая ее автоматически ошибочной. В C это не работает, поскольку перегрузка функций не поддерживается. И вы не можете проверить значение в функции и вызвать ошибку времени компиляции, поскольку все значения известны только во время выполнения (в отличие от типов).

Стандарт C позволяет компиляторам предупреждать о том, что вы делаете. Таким образом, вы можете включить -Wall -Werrorфлаг и надеяться, что gcc выдаст ошибку. Но это не общий способ C.

0
13.12.2008 03:41:37

В C типы enum неотличимы от целых чисел. Очень назойливый.

Единственный способ продвинуться вперед, о котором я могу думать, - это хитрый обходной путь, использующий структуры вместо перечислений. Структуры являются генеративными, поэтому сотни и тысячи различны. Если соглашение о вызовах является разумным (AMD64), не будет никаких накладных расходов во время выполнения.

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

#include <stdio.h>
enum hundred_e {
    VALUE_HUNDRED_A = 100,
    VALUE_HUNDRED_B
};

enum thousand_e {
    VALUE_THOUSAND_A = 1000,
    VALUE_THOUSAND_B
};

struct hundred { enum hundred_e n; };
struct thousand { enum thousand_e n; };

const struct hundred struct_hundred_a = { VALUE_HUNDRED_A }; 
const struct hundred struct_hundred_b = { VALUE_HUNDRED_B }; 
const struct thousand struct_thousand_a = { VALUE_THOUSAND_A }; 
const struct thousand struct_thousand_b = { VALUE_THOUSAND_B }; 

void print_hundred(struct hundred foo)
{
    switch (foo.n) {
        case VALUE_HUNDRED_A:     printf("hundred:a\n");     break;
        case VALUE_HUNDRED_B:     printf("hundred:b\n");     break;
        default: printf("hundred:error(%d)\n", foo.n); break;
    }
}

void print_thousand(struct thousand bar)
{
    switch (bar.n) {
        case VALUE_THOUSAND_A:     printf("thousand:a\n");     break;
        case VALUE_THOUSAND_B:     printf("thousand:b\n");     break;
        default: printf("thousand:error(%d)\n", bar.n); break;
    }
}

int main(void)
{

    print_hundred(struct_hundred_a);
    print_hundred(struct_thousand_a);  /* Want a compile error here */

    print_thousand(struct_thousand_a);
    print_thousand(struct_hundred_a);  /* Want a compile error here */

    return 0;
}
10
13.12.2008 06:18:14

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

Если этот тип проверки ошибок важен для вас, тогда я предлагаю исследовать линкеры C / средства проверки стиля / инструменты статического анализа, которые улавливают эту и другие распространенные (и не очень распространенные) ошибки (если вы их правильно настроили!). Добавление этих инструментов в ваш процесс сборки - это небольшая работа, но если для вашего проекта вы считаете, что отлов такого рода вещей при компиляции является ценным, тогда стоимость того стоит.

Два я бы порекомендовал:

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

Альтернативой с открытым исходным кодом будет Splint , но, к сожалению, в данный момент она в основном не поддерживается.

Есть более дорогие коммерческие инструменты, такие как Klocwork и Coverity.

Добавление таких инструментов в ваше программное обеспечение требует определенных усилий. Они, как правило, чрезвычайно гибкие и настраиваемые, и вам необходимо принять взвешенные решения о том, какое поведение вы хотите разрешить, а какие - запретить в своей базе кода.

0
14.12.2008 11:53:25

Вы можете сделать это, используя #defines для ваших функций и __builtin_constant (x), который возвращает 1, если x разрешает константу, и 0, если нет. Обратите внимание, что это только встроенная функция gcc; Я понятия не имею, есть ли эквиваленты на других компиляторах.

0
14.12.2008 13:05:19