Подтвердить что ты не робот

Отчет об ошибках в библиотеке C

Я ищу надежный способ сообщить об ошибках в библиотеке 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 довольно уродливым.

Итак, что вы берете на себя? Как вы сообщаете об ошибках чистым способом?

4b9b3361

Ответ 1

int get_data(void **ptr)

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

Ответ 2

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

Предложение # 1. Используйте пользовательские errnos. Я знаю, что вы указали, что предпочли бы не использовать это. Я понимаю, что вы обеспокоены тем, что 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 при успехе с настраиваемым ненулевым значением при возврате и позволяют одному из параметров быть указателем на значение, которое вы хотите установить. Ошибки могут быть легко обнаружены; однако документирование их может быть проблематичным, если у вас есть глубокие деревья вызовов, которые используют эту методологию.

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

Ответ 3

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

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

Ответ 4

Наличие аргумента "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), чтобы проверить, произошла ли какая-либо ошибка.

Ответ 5

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