Иногда я должен написать код, который чередуется между выполнением вещей и проверкой состояния ошибки (например, назовите библиотечную функцию, проверьте ее возвращаемое значение, продолжайте идти). Это часто приводит к длительным периодам, где фактическая работа происходит в условиях если операторы, как
if(! (data = (big_struct *) malloc(sizeof(*data)))){
//report allocation error
} else if(init_big_struct(data)){
//handle initialization error
} else ...
Как делают Вас, парни пишут этот вид кода? Я проверил несколько руководств по стилю, но они кажутся более соответствующими с переменным именованием и пробелом.
Ссылки на приветствующиеся руководства по стилю.
Править: в случае, если это не ясно, я неудовлетворен четкостью этого стиля и поиска чего-то лучше.
Хотя мне больно это говорить, это может быть случай никогда не пользовавшегося популярностью goto
. Вот одна ссылка, которую я нашел по этой теме: http://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c/
Один метод, который я использовал с большим успехом, - это тот, который использовал У. Ричард Стивенс в Сетевое программирование Unix (код можно загрузить здесь . Для общих функций, которые, как он ожидает, все время будут успешными и у которых нет возможности обратиться в случае сбоя, он заключает их в оболочку, используя заглавную букву (код сжат по вертикали):
void * Malloc(size_t size) {
void *ptr;
if ( (ptr = malloc(size)) == NULL)
err_sys("malloc error");
return(ptr);
}
err_sys
здесь отображает ошибка, а затем выполняет exit (1)
. Таким образом, вы можете просто вызвать Malloc и знать, что он выдаст ошибку, если возникнет проблема.
UNP продолжает оставаться единственной книгой, которую я где, я думаю, у автора есть код, который проверяет возвращаемые значения всех функций, которые могут потерпеть неудачу. В каждой другой книге говорится, что «вы должны проверить возвращаемые значения, но мы оставим это вам, чтобы сделать это позже».
Я предпочитаю
longjmp
. (На самом деле я использую интерфейсы и реализации C Дейва Хэнсона для имитации исключений.) Другой вариант - использовать грамотное программирование Дона Кнута для управления кодом обработки ошибок или что-то другое. своего рода препроцессор. Эта опция доступна только в том случае, если вы можете установить правила для своего магазина: -)
Я обычно пишу этот код таким образом:
data = (big_struct *) malloc(sizeof(*data));
if(!data){
//report allocation error
return ...;
}
err = init_big_struct(data);
if(err){
//handle initialization error
return ...;
}
...
Таким образом я избегаю вызова функций внутри if, а отладка упрощается, потому что вы можете проверить возвращаемые значения.
Единственное группирующее свойство такого кода состоит в том, что существует просто внешне навязанная последовательность, которой он должен следовать. Вот почему вы помещаете эти выделения в одну функцию, но это очень слабая общность. Почему некоторые люди рекомендуют отказаться от преимуществ вложенных if, я не понимаю. Вы эффективно пытаетесь накрасить свинью помадой (без оскорбления) - природа кода никогда не даст ничего чистого, лучшее, что вы можете сделать, - это использовать компиляторы, помогающие отлавливать (обслуживающие) ошибки. ИМХО, придерживайтесь «если».
PS: если я еще не убедил вас: как будет выглядеть решение goto, если вам придется принимать тройные решения? Если, конечно, станет еще уродливее, но вот goto ???
Не используйте assert
в production-коде.
В режиме отладки assert
никогда не должен использоваться для того, что может произойти (например, malloc
возвращает NULL
), скорее он должен использоваться в невозможных случаях (например, индекс массива выходит за границы в C
)