C или C ++ Статус возврата - PullRequest
       11

C или C ++ Статус возврата

6 голосов
/ 17 сентября 2011

Каковы лучшие практики для написания функций C или C ++, которые возвращают int, представляющий код состояния?

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

Например, могу ли я написать что-то вроде этого:

int foo() {
  return 0;  // because everything was cool
}

А потом использовать это так?

if (foo()) {
  // what to do if false, e.g. non-zero, e.g. not OK
} else {
  // what to do if true, e.g. zero, e.g. OK
}

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

Однако, это не было бы хорошо, верно:

if (!foo()) {
  // what to do if true
} else {
  // what to do if false
}

Ответы [ 7 ]

9 голосов
/ 17 сентября 2011

Мы используем это в C, где я работаю:

int err = foo();
if (err) {
    // armageddon
}

Назначение и, если возможно, могут быть объединены, но с более сложными вызовами функций это становится более запутанным, и некоторые люди путаются в назначении в условном выражении (и gcc ненавидит это.)

Для C ++ я бы предпочел исключения, если они доступны, в противном случае - выше.

Редактировать: я бы рекомендовал возвращать 0 в случае успеха и все остальное в случае ошибки.Это то, что делают утилиты командной строки Unix.

5 голосов
/ 17 сентября 2011

Если вы действительно хотите использовать коды состояния таким образом, используйте их с enum или блоком #define операторов, которые описывают намерение кода состояния.

Дляпример:

enum
{
   kSuccess = 0,
   kFailure = -1,
}

function foo()
{
    return kSuccess;
}

if (kSuccess == foo())
{
    // Handle successful call to foo
}
else
{
    // Handle failed call to foo
}

Таким образом, цель ясна, и нет склонности к ошибкам, когда кто-то хочет использовать или поддерживать ваш код в будущем.

3 голосов
/ 17 сентября 2011
if (foo()) {
  // what to do if false
} else {
  // what to do if true
}

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

if(foo1()) {
    if(foo2()) {
        if(foo3()) {
            // the rest of your code
        } else {
            // handle error
        }
    } else {
        // handle error
    }
} else {
    // handle error
}

Чтобы решить проблему избыточного вложения, инвертируйте возвращаемое значение:

if(!foo1()) {
    // handle error
    return;
}

if(!foo2()) {
    // handle error
    return;
}

if(!foo3()) {
    // handle error
    return;
}

Это решение страдает от другой проблемы. Он смешивает логику программы с кодом обработки ошибок. Это все усложняет. В идеале вы хотите разделить логику программы и обработку ошибок. Эта проблема может быть исправлена ​​с помощью goto

if(!foo1()) 
    goto error1;

if(!foo2())
    goto error2;

if(!foo3())
    goto error3;

return;

error1:
    // handle error
    return;
error2:
    // handle error
    return;
error3:
    // handle error
    return;

Много чище.

Кроме того, goto может решить проблему освобождения ресурсов. См. Использование goto для обработки ошибок в C Эли Бендерским для получения дополнительной информации.

1 голос
/ 17 сентября 2011

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

1 голос
/ 17 сентября 2011

Статусы возврата должны быть определены в вашем интерфейсе и известны вызывающей стороне. Некоторые возвращают 0 при ошибке (потому что это легко проверить с помощью !), некоторые возвращают 0 при успехе (потому что они имеют enum кодов ошибок, при этом OK является первым элементом).

Нет закона или стандарта, каждый интерфейс определяет свои собственные соглашения. В C ++ - использовать исключения.

0 голосов
/ 17 сентября 2011

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

enum fooret { GOOD, BAD, UGLY, WORSE };

fooret foo();  // defined elsewhere

switch(foo())
{
case BAD:
case UGLY:
   // maybe a recoverable failure(s)...
   // take appropriate actions
   break;
case WORSE:
   // maybe non-recoverable
   break;
case GOOD:
   // successful, take appropriate actions
   break;
}
0 голосов
/ 17 сентября 2011
int foo() {
   try{
    ...
   return 1
   }
   catch
   {
   return 0;  // because everything was cool
   }
}

Я бы начал с упаковки всего в блок try / catch.Также вместо использования и int это может создать больше сцены для возврата логического значения.Это немного более интуитивно понятно при тестировании в операторе if.

...