Примерно 90% встроенных систем вокруг вас запрограммированы на C, на компиляторах C, без поддержки C ++. (Остальные 10% будут АСМ или АДА). Существует целый мир за пределами вашего ПК.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответы на вопрос(7)

Решение Вопроса

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.

 ruslik23 янв. 2011 г., 21:48
Глобальная переменная может быть сделана глобальной для каждого потока (TLS в Win32).
 ruslik23 янв. 2011 г., 21:52
Я знаю, что они доступны на большинстве платформ, но я знал только название для Windows.GetLastError() использует этот подход.
 Jonathan Leffler23 янв. 2011 г., 21:51
@ruslik: вы можете использовать TLS (локальное хранилище потоков) и в системах Unix; тем не менее, он все еще не вполне удовлетворителен.

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

Итак, тогда:

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. Организуйте свой вычислительный код в классе, затем добавьтедействует() метод:

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;
 ern023 янв. 2011 г., 22:32
Эй, парни, это 2011, покажи мне компилятор C, который не поддерживает C ++. Или покажи мне программиста на C, который не использует C ++ (ошибайся ... не делай, не расстраивай меня).
 Oliver Charlesworth23 янв. 2011 г., 22:07
-1: С, чувак. Кроме того, OP специально упомянул, что любое возвращаемое значение может быть допустимым в некоторых случаях.
 Lundin24 янв. 2011 г., 08:44
Примерно 90% встроенных систем вокруг вас запрограммированы на C, на компиляторах C, без поддержки C ++. (Остальные 10% будут АСМ или АДА). Существует целый мир за пределами вашего ПК.
 Jonathan Leffler23 янв. 2011 г., 22:06
Большинство компиляторов C не поддерживаютclass', как ни странно. Это рассматривается как функция C ++. Вопрос помечен C, а не C ++.

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

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

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

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

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

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

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

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

    /* error */
    return NULL;
}

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

 Lundin24 янв. 2011 г., 08:47
Хех, вы не могли бы сделать его более общим? Я предполагаю, что этот пост был шуткой.
 Peyman24 янв. 2011 г., 09:13
О, вы можете сделать его более общим: void * foo ();

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

int func (Error_t * error);

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

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

 Jonathan Leffler23 янв. 2011 г., 22:09
INT_MIN может быть лучше; в любом случае он асимметричен с INT_MAX и оставляет рабочий диапазон -INT_MAX .. + INT_MAX, который симметричен относительно нуля. На компьютере с двумя дополнениями INT_MIN имеет шаблон 0x8000, 0x80000000 или 0x8000000000000000 в зависимости от размера типа int (показаны 2, 4, 8 байтов, при условии, что я считал правильно).

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

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

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

 peoro23 янв. 2011 г., 22:24
@ruslik: да, но это требует изменения всех его функций
 ruslik23 янв. 2011 г., 21:50
Наиболее однородный интерфейс - всегда возвращать код ошибки (с 0 для успеха) и возвращать оставшиеся значения по ссылке.

Ваш ответ на вопрос