Отчет об ошибках в библиотеке C - PullRequest
20 голосов
/ 01 июля 2011

Я ищу надежный способ сообщить об ошибках в библиотеке C.Рассмотрим простой пример очереди:

struct queue *q = malloc(sizeof(*q));
if (NULL == q) {
    /* malloc failed. now what ? */
    return NULL; /* maybe ? */
}

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

void *get_data()
{
    /* stuff */

    /* Error detected. NULL is a valid return, now what ? */

    /* stuff */
}

Более того, когда мы сообщаем об ошибке, как сигнализировать , что это ошибка?Я думал об этом, и у меня нет удовлетворительного решения.

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

  • Я думал о том, чтобы заставить клиента предоставить некоторый объект «статуса», который можно будет проверить после вызова, но это сделает API довольно уродливым.*

Итак, что вы думаете по этому поводу?Как вы сообщаете об ошибках в чистом виде?

Ответы [ 5 ]

6 голосов
/ 01 июля 2011
int get_data(void **ptr)

Если нет явных «возвратов ошибок», то, возможно, ваше выходное значение не должно быть возвращаемым значением. Ошибка может быть либо ошибкой, каким-либо другим пользовательским подробным значением ошибки (* cough * HRESULT), просто true / false, если функция выполнена успешно, либо некоторой другой полезной информацией (длина данных, или -1, если ошибка )

6 голосов
/ 01 июля 2011

У меня есть пара предложений.

Предложение # 1 - Использовать пользовательские ошибки.Я знаю, что вы указали, что предпочли бы не использовать это.Я понимаю, что вы обеспокоены тем, что errno будет забит в многопоточной среде, но я ожидаю, что каждый поток должен иметь свое собственное хранилище для errno.Следующая ссылка http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?errno+3 предполагает, что это довольно просто сделать.

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

eg.
#define MODULE_NAME_error_code ((MODULE_NUMBER << 16) | (error_code))

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

Предложение # 2 - Пусть ваши подпрограммы возвращают 0в случае успеха с пользовательским ненулевым значением при возврате, и пусть один из параметров будет указателем на значение, которое вы хотите установить.Ошибки могут быть легко обнаружены;однако их документирование может быть проблематичным, если у вас есть глубокие деревья вызовов, использующие эту методологию.

Надеюсь, это поможет.

4 голосов
/ 01 июля 2011

Если вы действительно хотите многопоточный, повторяющийся способ сообщения об ошибках, я думаю, вы не можете избежать, при каждом вызове lib, передавать указатель на структуру «status». Эта структура будет иметь среди других вещей состояния объекта результат рассматриваемого вызова.

Это действительно ужасно, но не обязательно плохо.

3 голосов
/ 01 июля 2011

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

Каждый бит в переменной состояния будет представлять отдельную ошибку, поэтому, если возвращается NULL, вызывающая сторона должна проверить, status & ERR_NOMEM или status & ERR_IO и т. Д. Маски ошибок могут быть определены следующим образом:

#define ERR_NOMEM (1 << 0)
#define ERR_IO    (1 << 1)
...

Установка соответствующей ошибки внутри функций может быть выполнена как status |= ERR_IO.

Это даже дает вам возможность иногда указывать более одной ошибки - вызывающий может проверить status & (ERR_NOMEM | ERR_IO), чтобы проверить, произошла ли какая-либо из ошибок.

0 голосов
/ 01 июля 2011

Моя первая мысль: почему бы не использовать stderr, заполнив его сообщениями, указывающими источник и причину проблемы?Или, может быть, я пропустил ваше намерение:)

...