Я несколько раз сталкивался с вопросами и ответами и хотел дать более полный ответ. Я думаю, что лучший способ думать об этом - как вернуть ошибки вызывающей стороне и что вы вернете.
Как
Есть 3 способа вернуть информацию из функции:
- Возвращаемое значение
- Аргумент (ы)
- Out of Band, который включает нелокальное goto (setjmp / longjmp), файловые или глобальные переменные области действия, файловую систему и т. Д.
Возвращаемое значение
Вы можете вернуть значение только одного объекта, однако это может быть произвольный комплекс. Вот пример функции возврата ошибки:
enum error hold_my_beer();
Одним из преимуществ возвращаемых значений является то, что они позволяют объединять вызовы для менее навязчивой обработки ошибок:
!hold_my_beer() &&
!hold_my_cigarette() &&
!hold_my_pants() ||
abort();
Это не только о удобочитаемости, но также может позволить обрабатывать массив таких указателей на функции унифицированным способом.
Аргумент (ы)
Вы можете возвращать больше с помощью более чем одного объекта с помощью аргументов, но передовая практика предлагает сохранить общее количество аргументов низким (скажем, <= 4):
void look_ma(enum error *e, char *what_broke);
enum error e;
look_ma(e);
if(e == FURNITURE) {
reorder(what_broke);
} else if(e == SELF) {
tell_doctor(what_broke);
}
Из группы
С помощью setjmp () вы определяете место и то, как вы хотите обработать значение int, и вы передаете управление этому месту с помощью longjmp (). См. Практическое использование setjmp и longjmp в C .
какой
- Показатель
- Код
- объект
- Перезвони
Показатель
Индикатор ошибки только говорит о том, что есть проблема, но ничего о природе указанной проблемы:
struct foo *f = foo_init();
if(!f) {
/// handle the absence of foo
}
Однако это наименее эффективный способ для функции сообщать о состоянии ошибки, однако он идеально подходит, если вызывающий в любом случае не может ответить на ошибку постепенно.
Код
Код ошибки сообщает вызывающей стороне о природе проблемы, и может позволить подходящий ответ (из вышеупомянутого). Это может быть возвращаемое значение или как пример look_ma () над аргументом ошибки.
объект
При наличии объекта ошибки вызывающий может быть проинформирован о произвольных сложных проблемах. Например, код ошибки и подходящее удобочитаемое сообщение. Он также может сообщить вызывающей стороне, что несколько вещей пошли не так, или ошибка на элемент при обработке коллекции:
struct collection friends;
enum error *e = malloc(c.size * sizeof(enum error));
...
ask_for_favor(friends, reason);
for(int i = 0; i < c.size; i++) {
if(reason[i] == NOT_FOUND) find(friends[i]);
}
Вместо того, чтобы предварительно выделять массив ошибок, вы также можете (пере) распределять его динамически по мере необходимости.
Перезвони
Обратный вызов является наиболее эффективным способом обработки ошибок, поскольку вы можете сообщить функции, какое поведение вы хотели бы видеть, когда что-то идет не так. Аргумент обратного вызова может быть добавлен к каждой функции, или если настройка требуется только для экземпляра структуры, подобной этой:
struct foo {
...
void (error_handler)(char *);
};
void default_error_handler(char *message) {
assert(f);
printf("%s", message);
}
void foo_set_error_handler(struct foo *f, void (*eh)(char *)) {
assert(f);
f->error_handler = eh;
}
struct foo *foo_init() {
struct foo *f = malloc(sizeof(struct foo));
foo_set_error_handler(f, default_error_handler);
return f;
}
struct foo *f = foo_init();
foo_something();
Одним интересным преимуществом обратного вызова является то, что он может быть вызван несколько раз или вообще не вызываться при отсутствии ошибок, при которых нет никаких издержек на счастливом пути.
Существует, однако, инверсия контроля. Код вызова не знает, был ли вызван обратный вызов. Таким образом, может иметь смысл также использовать индикатор.