Как вызвать ошибку компиляции, если возвращаемое значение функции не проверено? - PullRequest
3 голосов
/ 15 октября 2019

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

Я использую GCC в eclipse.

Например:

int fun (){ return 3;}

void main ()
{
   printf("%d",fun ());
}

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

int ret=fun();
if(ret != some_value) { /* Do something */ }
printf("%d",fun ());

Возможно ли это?

1 Ответ

5 голосов
/ 15 октября 2019

Вы не можете принудительно выполнить проверку

Я не думаю, что есть способ сделать это так, как вы этого хотите. В конце концов, в операторе printf("%d",fun ()) вы на самом деле проверяете возвращаемое значение из fun(), отправляя его в другую функцию. Конечно, printf на самом деле не «проверяет» возвращаемое значение, но вы могли бы использовать его следующим образом:

void exit_if_fun_returns_negative_value(int val) { if(val<0) exit(EXIT_FAILURE); }

int main(void)
{
    exit_if_fun_returns_negative_value(fun());
}

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

char *array;
void *ptr;
ptr = realloc(array, 20);
strcpy(array, "Hello, World!");

Обратите внимание, как мы сохраняем возвращаемое значение в ptr, но поскольку мы не делаем что-то вроде if(ptr == NULL) exit(EXIT_FAILURE);, это довольно бессмысленно.

НоВы можете запретить полное игнорирование возвращаемого значения

Однако есть способ предотвратить операторы, в которых вы не используете (что, конечно, не то же самое, что проверка на самом деле) возвратазначение на всех.

Насколько я знаю, нет портативного способа сделать это. Вам придется полагаться на расширения компилятора. Gcc имеет такое расширение.

__attribute__ ((warn_unused_result)) int foo (void) 
{
    return 5;
}

int main(void)
{
    foo();
}

Компиляция этого сгенерирует это предупреждение:

$ gcc main.c
main.c: In function ‘main’:
main.c:9:5: warning: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Wunused-result]
    9 |     foo();
      |     ^~~~~

Для того, чтобы рассматривать это как ошибку, скомпилируйте с -Werror

$ gcc main.c -Werror
main.c: In function ‘main’:
main.c:9:5: error: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Werror=unused-result]
    9 |     foo();
      |     ^~~~~
cc1: all warnings being treated as errors

Если вы хотите, чтобы все остальные предупреждения были просто предупреждениями, скомпилируйте с -Werror=unused-result. Пример:

$ cat main.c 
__attribute__ ((warn_unused_result))
int foo (void)
{
    return 5;
}

int main(void)
{
    int *x = 5;
    foo();
}

$ gcc main.c -Werror=unused-result
main.c: In function ‘main’:
main.c:9:14: warning: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
    9 |     int *x = 5;
      |              ^
main.c:10:5: error: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Werror=unused-result]
   10 |     foo();
      |     ^~~~~
cc1: some warnings being treated as errors

Измените сигнатуру, включив в нее выходной параметр

Один из вариантов для достижения чего-то похожего - перенести возвращаемое значение в параметр. Это заставит сохранить «возвращаемое значение» в параметре, и вы не сможете вызвать функцию, не отправив ей выходную переменную. Измените

int fun (){ return 3;} 

на

void fun(int *ret) { *ret=3; }

Но, как я уже упоминал выше, я не могу понять, как вы можете принудительно проверить переменную. Это только принудительно назначает.

Функции оболочки

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

.h

int fun_wrapper();

.c

int fun() { return 3; }

int fun_wrapper() 
{ 
    int ret = fun(); 
    if(ret<0) exit(EXIT_FAILURE);
    return ret;
}

Как использовать эту технику для библиотечных функций

Я создалсвязанный вопрос, у которого есть ответ. Речь идет о том, как использовать это для fgets и других функций из библиотеки: Как создать оболочки для функций библиотеки с оригинальным именем?

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