В свете дискуссии
здесьАвтор: Maxim S. Shatskih
Дата: 11.06.04
возник интересный вопрос: как вы оформляете код на plain C, который проверяет возвращаемый код из цепочки вызываемых функций?
Я в своей практике встречал следующие варианты:
1. "Ёлочка":
int f()
{
int failed;
void *obj1;
void *obj2;
failed = construct_obj1(&obj1);
if (!failed) {
failed = construct_obj2(&obj2);
if (!failed) {
failed = do_something(obj1, obj2);
// Обработка результатов вызова do_something
destruct_obj2(obj2);
}
destruct_obj1(obj1);
}
return failed;
}
Плюсы:
выделена основная ветка кода;
при добавлении в цепочку новых вызовов необходимо вносить минимум изменений;
Минусы:
код интенсивно уползает вправо;
если необходимо выйти из функции не через конечный return, то приходится вручную вызывать все деструкторы;
2. "goto в конец функции":
int f()
{
int failed;
void *obj1;
void *obj2;
failed = construct_obj1(&obj1);
if (failed) {
goto end1;
}
failed = construct_obj2(&obj2);
if (failed) {
goto end2;
}
failed = do_something(obj1, obj2);
// Обработка результатов вызова do_something
destruct_obj2(obj2);
end2:
destruct_obj1(obj1);
end1:
return res;
}
Плюсы:
Минусы:
нелюбимый многими goto 
при добавлении в цепочку новых вызовов можно легко забыть добавить деструктор или переход на нужную метку;
если необходимо выйти из функции не через конечный return, то приходится вручную вызывать все деструкты;
3. "выходим сразу":
int f()
{
int failed;
void *obj1;
void *obj2;
failed = construct_obj1(&obj1);
if (failed) {
return failed;
}
failed = construct_obj2(&obj2);
if (failed) {
destruct_obj1(obj1);
return failed;
}
failed = do_something(obj1, obj2);
// Обработка результатов вызова do_something
destruct_obj2(obj2);
destruct_obj1(obj1);
return failed;
}
Плюсы:
Минусы:
при добавлении в цепочку новых вызовов приходится добавлять деструкторы как минимум в 2 места;
4. "выделение деструкторов в отдельный блок":
void finallize_f(void* obj1, void* obj2)
{
if (obj2 != NULL) {
destruct_obj2(obj2);
}
if (obj1 != NULL) {
destruct_obj1(obj1);
}
}
int f()
{
int failed;
void *obj1 = NULL;
void *obj2 = NULL;
failed = construct_obj1(&obj1);
if (failed) {
finallize_f(obj1, obj2);
return failed;
}
failed = construct_obj2(&obj2);
if (failed) {
finallize_f(obj1, obj2);
return failed;
}
failed = do_something(obj1, obj2);
// Обработка результатов вызова do_something
finallize_f(obj1, obj2);
return failed;
}
Плюсы:
код не уползает вправо
разрушение объектов сосредоточено в одном месте, из функции f можно корректно выйти из любой точки — достаточно вызвать finallize_f;
Минусы:
лишняя функция;
проверки на NULL режут глаз; 
Ну и, напоследок, вариант на C++:
Obj1 obj1;
Obj2 obj2;
do_something(obj1, obj2);