Ошибка проверки функции, которая возвращает int - PullRequest
6 голосов
/ 23 января 2011

Если у меня есть функция, которая возвращает какой-то указатель, я проверяю ошибки, устанавливая возвращаемое значение NULL в случае ошибки.

char *foo(void) {
  //If stuff doesn't go okay
  return NULL;
}

char *bar = foo();
if(!bar) return 1;

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

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

Пара обходных путей:

  1. Включить параметр кода ошибки в функцию
  2. Возвращает код ошибки и включает указатель int в качестве параметра

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

Есть ли другое решение, которое не предполагает смену интерфейса на функцию? Какой самый распространенный способ справиться с этой ситуацией?

ВЫБРАННОЕ РЕШЕНИЕ

Спасибо за все ваши мысли и ответы на этот вопрос.

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

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

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

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

Ответы [ 7 ]

4 голосов
/ 23 января 2011

Это ужасное решение глобальной переменной - аналог errno. Не рекомендуется.

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

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

if ((rc = your_function(in1, in2, &out)) != 0)
    ...handle error...
else
    ...use returned value...

Это, вероятно, наименее противное решение.

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

Error_t err;

int result = your_function(in1, in2, &err);
if (err.code != 0)
    ...handle error...
else
    ...use returned value...

Вы не можете проверить состояние ошибки в той же строке, что и вызываемая функция, что некоторые расценили бы как неприятность. Это особенно важно, когда вы находитесь в третьем предложении последовательности операций else if - тогда вы должны ввести новый уровень вложенности, где код ошибки в качестве возвращаемого значения не требует этого. В свою очередь, информация в структуре ошибок может быть более полной, чем один номер ошибки, что может привести к улучшению диагностики.

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

3 голосов
/ 23 января 2011

Лучший и наиболее распространенный способ (практикуемый, например, Windows API) - написать всю обработку ошибок следующим образом:

int func (Error_t * error);

где Error_t - это какая-то индикация ошибки. Таким образом, это не повлияет на возвращаемый результат.

2 голосов
/ 23 января 2011

Вы можете просто пожертвовать самым большим отрицательным или положительным числом для типа int, такого как INT_MAX. Просто создайте const MY_INT_ERROR_VALUE и сравните с ним.

1 голос
/ 24 января 2011

Как насчет этого:

void *foo(void *res)
{
    // cast the void *res to whatever type the result would be
    if (/* successfull */) 
        return res;

    /* error */
    return NULL;
}

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

1 голос
/ 23 января 2011

Есть несколько способов сделать то, что вы хотите, но все они приведут вас к неоднородному интерфейсу (или к списку неоднородного способа проверки на ошибки).

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

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

0 голосов
/ 26 марта 2011

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

int get_value(int *value)
{
    if ( input_ok )
        *value = input;
        return 0;
    return -1;
}

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

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

так что допустите ошибку: не используйте специальное значение и используйте выделенный путь обработки ошибок.

обратите внимание, что вы можете изменить приведенный выше код и написать int get_value(int *error);

0 голосов
/ 24 января 2011

1. Используйте NaN, как плавающие сопроцессоры.Выберите значение, которое может никогда не использоваться в качестве результата, чтобы указать ошибку.

# define VALUE_OF_FAIL (-99999)

Итак:

char foo(void) {   
  // If stuff doesn't go okay   
  return VALUE_OF_FAIL; 
}

char bar = foo(); 
if (bar == VALUE_OF_FAIL) return 1;

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

2. Организуйте свой вычислительный код в классе, затем добавьте isValid () метод:

class FooBar {

  public FooBar() {  // constructor
    this.fail = false;   // initialize here...
  }

  char foo(void) {  // the computation method

    this.fail = false;  // ...or here

    // If stuff doesn't go okay   
    this.fail = true;  // or error code
    return 0; // any value
  }

  bool failed() {
    return this.fail; 
  }

}

// outside the class    
fb = new FooBar();
char bar = fb->foo(); 
if (fb->failed()) return 1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...