Оформление обработки ошибок в plain C
От: postmaster  
Дата: 16.06.04 09:41
Оценка:
В свете дискуссии здесь
Автор: 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;
}

Плюсы:
Минусы:

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;
}

Плюсы:
Минусы:

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;
}

Плюсы:
Минусы:

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;
}

Плюсы:
Минусы:

Ну и, напоследок, вариант на C++:
Obj1 obj1;
Obj2 obj2;
do_something(obj1, obj2);
Re: Оформление обработки ошибок в plain C
От: rus blood Россия  
Дата: 16.06.04 10:44
Оценка:
Коммент по поводу пункта 2.

P>2. "goto в конец функции":

P>
.....
P>

P>Плюсы:
P> P>Минусы:
P>
Подобная схема применяется в коде ядра linux-а, и никто вроде особо не жаловался...



P>3. "выходим сразу":

P>
.................
P>

P>Плюсы:
P> P>Минусы:
P> Остойный вариант. Годится только для небольшого числа используемых "ресурсов" (один-два)


P>4. "выделение деструкторов в отдельный блок":

P>
..........
P>

То же самое, что и вариант 3. Чуть облагороженный.
Имею скафандр — готов путешествовать!
Re: Оформление обработки ошибок в plain C
От: TheCat Россия abokov.livejournal.com
Дата: 18.06.04 07:21
Оценка: 1 (1) +1
Здравствуйте, postmaster, Вы писали:

P>В свете дискуссии здесь
Автор: Maxim S. Shatskih
Дата: 11.06.04
возник интересный вопрос: как вы оформляете код на plain C, который проверяет возвращаемый код из цепочки вызываемых функций?


P>Я в своей практике встречал следующие варианты:


еще один вариант — "липовый" do {}while(0);
int f()
{
int failed;
void *obj1=NULL;
void *obj2=NULL;
do {
if (!(failed=construct_obj1(&obj1)))
break;
if (!(failed=construct_obj2(&obj2)))
break;
failed=do_something(obj1,obj2);
} while(false);

if (obj1!=NULL)
destruct_obj1(obj1);
if (obj2!=NULL)
destruct_obj2(obj1);
return failed;
}


плюсы :
+ в любом месте можно выйти из него простым break не заботясь о вызове деструкторов
+ все деструкторы вызываются в одном месте
+ несмотря на похожесть на вариант с goto тут нет меток
+ внутри do {} while() можно завести какой локальный объект внутри тела блока.
минусы :
— кому то может показаться что do {} while() используется не по делу)
— код все таки уползает вправо хотя и медленно.
— проверки на NULL режут глаз
Re[2]: Оформление обработки ошибок в plain C
От: aka50 Россия  
Дата: 18.06.04 07:39
Оценка:
Здравствуйте, TheCat, Вы писали:

TC>еще один вариант — "липовый" do {}while(0);

TC>int f()
TC>{
TC> int failed;
TC> void *obj1=NULL;
TC> void *obj2=NULL;
TC> do {
TC> if (!(failed=construct_obj1(&obj1)))
TC> break;
TC> if (!(failed=construct_obj2(&obj2)))
TC> break;
TC> failed=do_something(obj1,obj2);
TC> } while(false);

TC> if (obj1!=NULL)

TC> destruct_obj1(obj1);
TC> if (obj2!=NULL)
TC> destruct_obj2(obj1);
TC> return failed;
TC>}


TC>плюсы :

TC> + в любом месте можно выйти из него простым break не заботясь о вызове деструкторов
TC> + все деструкторы вызываются в одном месте
TC> + несмотря на похожесть на вариант с goto тут нет меток
TC> + внутри do {} while() можно завести какой локальный объект внутри тела блока.
TC> минусы :
TC> — кому то может показаться что do {} while() используется не по делу)
TC> — код все таки уползает вправо хотя и медленно.
TC> — проверки на NULL режут глаз

— если внутри есть еще цикл или case, то выход затруднителен... надо goto пользовать...
Re[2]: Оформление обработки ошибок в plain C
От: ssm Россия  
Дата: 18.06.04 07:52
Оценка:
Здравствуйте, TheCat, Вы писали:

TC>еще один вариант — "липовый" do {}while(0);


именно этот метод настоятельно рекомендуется для разработчиков расширений Adobe. в частности в Adobe InDesign SDK есть на то прямые указания.
Re[3]: Оформление обработки ошибок в plain C
От: ssm Россия  
Дата: 18.06.04 07:54
Оценка:
Здравствуйте, aka50, Вы писали:


A>- если внутри есть еще цикл или case, то выход затруднителен... надо goto пользовать...


надо правило одного выхода пользовать и стараться циклы выносить
Re[2]: Оформление обработки ошибок в plain C
От: barmafon  
Дата: 18.06.04 08:18
Оценка: 19 (4) +1
TC>еще один вариант — "липовый" do {}while(0);
TC>плюсы :
TC> + в любом месте можно выйти из него простым break не заботясь о вызове деструкторов
TC> + все деструкторы вызываются в одном месте
TC> + несмотря на похожесть на вариант с goto тут нет меток
TC> + внутри do {} while() можно завести какой локальный объект внутри тела блока.
TC> минусы :
TC> — кому то может показаться что do {} while() используется не по делу)
TC> — код все таки уползает вправо хотя и медленно.
TC> — проверки на NULL режут глаз

для устранения минуса №3 можно воспользоваться следующей техникой:
int f()
{
  int stage, failed;
  void *obj1;
  void *obj2;
  for( stage = 0;; ) {
    if( !(failed=construct_obj1(&obj1)) )
      break;
    ++stage;
    if( !(failed=construct_obj2(&obj2)) )
      break;
    ++stage;
    if( !(failed=do_something(obj1,obj2)) )
      break;
    ++stage;
    break;
  }

  switch( stage ) {
  case 3: break;
  case 2: destruct_obj2(obj1);
  case 1: destruct_obj1(obj1);
  }

  return failed;
}


плюсы:
+ красиво
+ мы не обязаны инициализировать указатели на создаваемые объекты
минусы:
— нужна специальная переменная stage
— отслеживание значений переменной stage в каждой точке инициализации
Re[3]: Оформление обработки ошибок в plain C
От: _nn_  
Дата: 19.06.04 14:23
Оценка:
Здравствуйте, barmafon, Вы писали:

TC>>еще один вариант — "липовый" do {}while(0);

TC>>плюсы :
TC>> + в любом месте можно выйти из него простым break не заботясь о вызове деструкторов
TC>> + все деструкторы вызываются в одном месте
TC>> + несмотря на похожесть на вариант с goto тут нет меток
TC>> + внутри do {} while() можно завести какой локальный объект внутри тела блока.
TC>> минусы :
TC>> — кому то может показаться что do {} while() используется не по делу)
TC>> — код все таки уползает вправо хотя и медленно.
TC>> — проверки на NULL режут глаз

B>для устранения минуса №3 можно воспользоваться следующей техникой:

B>
B>int f()
B>{
B>  int stage, failed;
B>  void *obj1;
B>  void *obj2;
B>  for( stage = 0;; ) {
B>    if( !(failed=construct_obj1(&obj1)) )
B>      break;
B>    ++stage;
B>    if( !(failed=construct_obj2(&obj2)) )
B>      break;
B>    ++stage;
B>    if( !(failed=do_something(obj1,obj2)) )
B>      break;
B>    ++stage;
B>    break;
B>  }

B>  switch( stage ) {
B>  case 3: break;
B>  case 2: destruct_obj2(obj1);
B>  case 1: destruct_obj1(obj1);
B>  }

B>  return failed;
B>}
B>


B>плюсы:

B>+ красиво
B>+ мы не обязаны инициализировать указатели на создаваемые объекты
B>минусы:
B>- нужна специальная переменная stage
B>- отслеживание значений переменной stage в каждой точке инициализации

Может воспользоватся макросами :
int f()
{
    void *obj1;
    void *obj2;
    BEGIN_ERROR()
        BEGIN_INIT()
            ERROR_INIT1(construct_obj1,&obj1)
            ERROR_INIT1(construct_obj2,&obj2)
            ERROR_INIT2(do_something,&obj1,&obj2)
        END_INIT()
        BEGIN_TEST()
            ERROR_TEST(3)
            ERROR_TEST1(2,destruct_obj2,&obj2)
            ERROR_TEST1(1,destruct_obj1,&obj1)
        END_TEST()
    END_ERROR()
}

Сами макросы :
#define STAGE_VAR \
    stage_unique_name__

#define FAILED VAR \
    failed_unique_name__

#define BEGIN_ERROR() \
    { \
        int STAGE_VAR=0; \
        int FAILED_VAR=0;

#define END_ERROR() \
        return FAILED_VAR; \
    }

#define BEGIN_INIT() \
        while(1) \
        {

#define END_INIT() \
            break; \
        }

#define ERROR_INIT0(f) \
    if(!(FAILED_VAR=f())) \
        break; \
    ++STAGE_VAR;

#define ERROR_INIT1(f,p1) \
    if(!(FAILED_VAR=f(p1))) \
        break; \
    ++STAGE_VAR;

#define ERROR_INIT2(f,p1,p2) \
    if(!(FAILED_VAR=f(p1,p2))) \
        break; \
    ++STAGE_VAR;

#define BEGIN_TEST() \
    switch(STAGE_VAR) \
    {

#define END_TEST() \
    }

#define ERROR_TEST(n) \
    case n: break;

#define ERROR_TEST0(n,f) \
    case n: f();

#define ERROR_TEST1(n,f,p1) \
    case n: f(p1);

#define ERROR_TEST2(n,f,p1,p2) \
    case n: f(p1,p2);
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Оформление обработки ошибок в plain C
От: duan Россия  
Дата: 19.06.04 14:41
Оценка:
Здравствуйте, rus blood, Вы писали:


RB>Подобная схема применяется в коде ядра linux-а, и никто вроде особо не жаловался...


Там это используется по большей части потому, что эти конструкции генерируют наиболее оптимальный код на x86. О чем в этих самых исходниках и написано.
Re[4]: Оформление обработки ошибок в plain C
От: TheCat Россия abokov.livejournal.com
Дата: 21.06.04 06:46
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Может воспользоватся макросами :

__>
__>int f()
__>{
__>    void *obj1;
__>    void *obj2;
__>    BEGIN_ERROR()
__>        BEGIN_INIT()
__>            ERROR_INIT1(construct_obj1,&obj1)
__>            ERROR_INIT1(construct_obj2,&obj2)
__>            ERROR_INIT2(do_something,&obj1,&obj2)
__>        END_INIT()
__>        BEGIN_TEST()
__>            ERROR_TEST(3)
__>            ERROR_TEST1(2,destruct_obj2,&obj2)
__>            ERROR_TEST1(1,destruct_obj1,&obj1)
__>        END_TEST()
__>    END_ERROR()
__>}
__>

__>Сами макросы :
__>[c]

ТОЛЬКО НЕ ЭТО!!!)))
все что угодно только не макросы!
#define NULL 0L
я еще могу принять но END_ERROR() это уже перебор.
лучше уж тогда goto.)))
Re[5]: Оформление обработки ошибок в plain C
От: _nn_  
Дата: 21.06.04 17:12
Оценка: +1
Здравствуйте, TheCat, Вы писали:

TC>Здравствуйте, _nn_, Вы писали:


__>>Может воспользоватся макросами :

__>>
__>>int f()
__>>{
__>>    void *obj1;
__>>    void *obj2;
__>>    BEGIN_ERROR()
__>>        BEGIN_INIT()
__>>            ERROR_INIT1(construct_obj1,&obj1)
__>>            ERROR_INIT1(construct_obj2,&obj2)
__>>            ERROR_INIT2(do_something,&obj1,&obj2)
__>>        END_INIT()
__>>        BEGIN_TEST()
__>>            ERROR_TEST(3)
__>>            ERROR_TEST1(2,destruct_obj2,&obj2)
__>>            ERROR_TEST1(1,destruct_obj1,&obj1)
__>>        END_TEST()
__>>    END_ERROR()
__>>}
__>>

__>>Сами макросы :
__>>[c]

TC>ТОЛЬКО НЕ ЭТО!!!)))

TC>все что угодно только не макросы!
TC>#define NULL 0L
TC>я еще могу принять но END_ERROR() это уже перебор.
TC>лучше уж тогда goto.)))

А чем вам так не нравятся макросы ?
Главное осторожно использовать и все будет в порядке.
http://rsdn.nemerleweb.com
http://nemerleweb.com
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.