Выход из функции при ошибке
От: menify Россия  
Дата: 26.12.02 09:03
Оценка:
Привет!

Может для кого-то это очевидно, но я раньше мучался этой проблемой.
Суть в том, что когда в функции создаются несколько обьектов, выделяется для них память.
И вдруг возникает какая либо неприятность (памяти не хватило, файла нет и т.п. )

Нужно корретно выйти из функции, осободив все ранее созданные обьекты.

например:

MSG*    new_msg(..)
{
    MSG*        msg;
    HEADER*     header;
    ADDR*       to;
    ADDR*       from;
    char*       body;
    
    header = (HEADER*) malloc( sizeof(HEADER) );
    if (header == NULL)
    {
        return NULL;
    }
    
    ...
    
    to = (ADDR*)malloc( sizeof(ADDR) );
    if (to == NULL)
    {
        free( header );                 // !!!
        return NULL;
    }
    
    ...
    
    from = (ADDR*)malloc( sizeof(ADDR) );
    if (from == NULL)
    {
        free( to );                     // !!!
        free( header );                 // !!!
        return NULL;
    }
    
    ...
    
    msg = (ADDR*)malloc( sizeof(MSG) );
    if (msg == NULL)
    {
        free( body );                   // !!!
        free( from );                   // !!!
        free( to );                     // !!!
        free( header );                 // !!!
        return NULL;
    }
 
    ...
    
    return msg;
}


По моему это не очень красивый и надежный метод. Елементарно можно забыть где-нибудь поставить free(..)
Много разных идей было как сделать "красивый" и надежный код.
И вот осенило — нужно вкусить "запретный плод" — goto
До этого свято веря, что все можно сделать и без него, даже елегантнее.

С помощью goto будет так:

MSG*    new_msg(..)
{
    MSG*        msg;
    HEADER*     header;
    ADDR*       to;
    ADDR*       from;
    char*       body;
    
    
    header = (HEADER*) malloc( sizeof(HEADER) );
    if (header == NULL)
    {
        goto label_fail_new_header;
    }
    
    ...
    
    to = (ADDR*)malloc( sizeof(ADDR) );
    if (to == NULL)
    {
        goto label_fail_new_to;
    }
    
    ...
    
    from = (ADDR*)malloc( sizeof(ADDR) );
    if (from == NULL)
    {
        goto label_fail_new_from;
    }
    
    ...
    
    msg = (ADDR*)malloc( sizeof(MSG) );
    if (msg == NULL)
    {
        goto label_fail_new_msg;
    }
 
    ...
    
    return msg;
    
    
    //-------------------------------------------------------//
    //          LABELS:                                      //
    
    label_fail_new_msg:
        free( body );                   // !!!
    label_fail_new_body:
        free( from );                   // !!!
    label_fail_new_from:
        free( to );                     // !!!
    label_fail_new_to:
        free( header );                 // !!!
    label_fail_new_header:
        
        return NULL;
}


Я это написал, потому-что может это кому-нибудь будет полезным.
Может кто-то предложит такой-же эффективный метод, но без goto?


А еще goto классно работает когда нужно быстро выйти из функции при этом что-то выполнив на последок

Главное не переуседствовать и не тыкать goto без везких оснований...

Истина всегда где-то посередине.
Всего доброго.
Re: Выход из функции при ошибке
От: ssm Россия  
Дата: 26.12.02 09:07
Оценка: 2 (1)
Здравствуйте, menify, Вы писали:


M>Я это написал, потому-что может это кому-нибудь будет полезным.

M>Может кто-то предложит такой-же эффективный метод, но без goto?



M>

M>А еще goto классно работает когда нужно быстро выйти из функции при этом что-то выполнив на последок

M>Главное не переуседствовать и не тыкать goto без везких оснований...


M>Истина всегда где-то посередине.


филосов

истина лежит в использовании, к примеру, std::auto_ptr
Re: Выход из функции при ошибке
От: vav Россия  
Дата: 26.12.02 09:08
Оценка:
А разве это не классический пример выгоды использования умных указателей?
Re[2]: Выход из функции при ошибке
От: jazzer Россия Skype: enerjazzer
Дата: 26.12.02 09:29
Оценка:
Здравствуйте, ssm, Вы писали:

ssm>истина лежит в использовании, к примеру, std::auto_ptr


Все он правильно говорит, это же С, а не С++, а форум у нас по С/С++, а не только по С++.
Другое дело, что ничего нового сказано не было, все и так на С этим пользуются :)))
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: Выход из функции при ошибке
От: ssm Россия  
Дата: 26.12.02 09:37
Оценка:
Здравствуйте, jazzer, Вы писали:

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


ssm>>истина лежит в использовании, к примеру, std::auto_ptr


J>Все он правильно говорит, это же С, а не С++, а форум у нас по С/С++, а не только по С++.

J>Другое дело, что ничего нового сказано не было, все и так на С этим пользуются

оно то конечно, с одной стороны, — да, если судить по названию форума,
но с другой, если судить по описанию /*Программирование на C++. No VCL, no MFC, pure C++ only.*/ то скорее всего, что — нет
Re: Выход из функции при ошибке
От: LOK Украина  
Дата: 26.12.02 11:14
Оценка:
Здравствуйте, menify, Вы писали:



    MSG*    new_msg(..)
    {
        MSG*        msg=NULL;
        HEADER*     header=NULL;
        ADDR*       to=NULL;
        ADDR*       from=NULL;
        char*       body=NULL;

           while (1) 
        {
        header = (HEADER*) malloc( sizeof(HEADER) );
        if (!header)        
            break;
        
        ...

        to = (ADDR*)malloc( sizeof(ADDR) );
        if(!to)
            break;
        ...

         from = (ADDR*)malloc( sizeof(ADDR) );
         if (!from)
             break;            
         
         ...

         msg = (ADDR*)malloc( sizeof(MSG) );
         if (!msg)
             break;            
        
         
         ...
         break;

        }            
        
        
        if(!msg)        
        {
                 if (body)
            free( body );                   // !!!
            if (from)
                free( from );                   // !!!
            if (to )
            free( to );                     // !!!
            if (header)
            free( header );                 // !!!
         }
        
        
        
         return msg;
    }
for {; 1.0 beta 4 ;Roxette — A Thing About You }
Re: Выход из функции при ошибке
От: kmn Украина  
Дата: 26.12.02 11:28
Оценка:
Здравствуйте, menify, Вы писали:

Удалено избыточное цитирование. -- ПК

M>Нужно корретно выйти из функции, осободив все ранее созданные обьекты. <...>

M>Елементарно можно забыть где-нибудь поставить free(..)
M>Много разных идей было как сделать "красивый" и надежный код. И вот осенило — нужно вкусить "запретный плод" — goto

если говорить о C то можно использовать __try __finally

MSG*    new_msg(..)
{
    MSG*        msg = NULL;
    HEADER*     header = NULL;
    ADDR*       to = NULL;
    ADDR*       from = NULL;
    char*       body = NULL;
    
    __try
    {
    header = (HEADER*) malloc( sizeof(HEADER) );
    if (header == NULL)
        __leave;

    
    ...
    
    to = (ADDR*)malloc( sizeof(ADDR) );
    if (to == NULL)
        __leave;
    
    ...
    
    from = (ADDR*)malloc( sizeof(ADDR) );
    if (from == NULL)
        __leave;
    
    ...
    
    msg = (ADDR*)malloc( sizeof(MSG) );
    if (msg == NULL)
        __leave;
 
    ...
    
    }
    __finally
    {
    
        if (body)   free( body );                   // !!!
        if (from)   free( from );                   // !!!
        if (to)     free( to );                     // !!!
        if (header) free( header );                 // !!!
    }
}


P.S. я не знаю, __try __finally (__except) стандарт С?
Re[3]: Выход из функции при ошибке
От: Аноним  
Дата: 26.12.02 11:30
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Все он правильно говорит, это же С, а не С++, а форум у нас по С/С++, а не только по С++.

J>Другое дело, что ничего нового сказано не было, все и так на С этим пользуются

Я даже C99 стандарт просмотрел — никаких try, finally — чего-то подобного.
Может плохо смотрел.
Может в других клонах чистого С кто видел ?
Re[2]: Выход из функции при ошибке
От: mrhru Россия  
Дата: 26.12.02 11:56
Оценка:
Здравствуйте, kmn, Вы писали:

...    
    header = (HEADER*) malloc( sizeof(HEADER) );
    to     = (ADDR*)malloc( sizeof(ADDR) );
    from   = (ADDR*)malloc( sizeof(ADDR) );
    msg    = (ADDR*)malloc( sizeof(MSG) );

    if (header == NULL) || (to == NULL) || (from == NULL) || (msg == NULL)
    {
        free( body );               
        free( from );               
        free( to );                 
        free( header ); 
        return NULL;
    }
 ...

}


Евгений.
Re[4]: Выход из функции при ошибке
От: Yuri Россия http://spbdetails.ru
Дата: 26.12.02 12:50
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Я даже C99 стандарт просмотрел — никаких try, finally — чего-то подобного.

А>Может плохо смотрел.
А>Может в других клонах чистого С кто видел ?

__try — __finally вещь очень удобная, но в стандарте этого нет, т.к. это

Microsoft Specific —>

The try-finally statement is a Microsoft extension to the C and C++ languages that enables 32-bit target applications to guarantee execution of cleanup code when execution of a block of code is interrupted. Cleanup consists of such tasks as deallocating memory, closing files, and releasing file handles. The try-finally statement is especially useful for routines that have several places where a check is made for an error that could cause premature return from the routine.

For more information on structured exception handling in general, see Exception Handling Topics (SEH).

END Microsoft Specific
Take it easy.
Re: Выход из функции при ошибке
От: Багер  
Дата: 26.12.02 21:06
Оценка:
Гоуту не просто так в игнор записали. Если памяти не хватило, то уже
без разницы когда проверять выделена она или нет. Т.е. все проверки
после всех выделений памяти спасут программера от пользования этим
страшным оператором.

if( !ptr )
return;
if( !ptr2 )
{
if( !ptr3 )
{
if( !ptr4 )
{
delete ptr3;
}
delete ptr2;
}
delete ptr;
}
Ваша программа работает корректно? Один звонок и я всё исправлю!

Делаю потенциальные фичи :))
Re[2]: Выход из функции при ошибке
От: menify Россия  
Дата: 26.12.02 23:14
Оценка:
Здравствуйте, ssm, Вы писали:


ssm>истина лежит в использовании, к примеру, std::auto_ptr

Я плохо знаю C++, что такое std::auto_ptr?
Как это будет выглядеть, хотя бы примерно?
Всего доброго.
Re[2]: Выход из функции при ошибке
От: menify Россия  
Дата: 26.12.02 23:31
Оценка:
Здравствуйте, kmn, Вы писали:

kmn>если говорить о C то можно использовать __try __finally

Можно говорить и о C++.

Так же можно обойтись и стандартными средствами:


MSG*    new_msg(..)
{
    MSG*        msg = NULL;
    HEADER*     header = NULL;
    ADDR*       to = NULL;
    ADDR*       from = NULL;
    char*       body = NULL;
    
    header = (HEADER*) malloc( sizeof(HEADER) );
    if (header != NULL)
    {
        ...
        
        to = (ADDR*)malloc( sizeof(ADDR) );
        if (to != NULL)
        {
            ...
            
            from = (ADDR*)malloc( sizeof(ADDR) );
            if (from != NULL)
            {
                ...
                
                msg = (ADDR*)malloc( sizeof(MSG) );
                if (msg != NULL)
                {
                    ...
                    
                    return msg;
                }
            }
        }
    }
    
    if (body)   free( body );                   // !!!
    if (from)   free( from );                   // !!!
    if (to)     free( to );                     // !!!
    if (header) free( header );                 // !!!
    
    return NULL;
}



Все равно с goto код выглядит красивее, понятнее и проще (быстрее)

Нет лишних конструкций и проверок.
Всего доброго.
Re[3]: Выход из функции при ошибке
От: menify Россия  
Дата: 26.12.02 23:42
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Другое дело, что ничего нового сказано не было, все и так на С этим пользуются

Я тоже так думал, но устаю видеть как программисты пишут такие конструкции.
Бывает, что дублируется по 10 строчек кода от условия к условию.
И это каким-то чудом проходит испекции кода

И главное! Как они говорят — "Ведь код работает!"
Всего доброго.
Re[2]: Выход из функции при ошибке
От: menify Россия  
Дата: 26.12.02 23:52
Оценка:
Здравствуйте, vav, Вы писали:

vav>А разве это не классический пример выгоды использования умных указателей?

Возможно. Я слабо представляю себе как их можно использовать?

Я говорю не только про выделение памяти, это может быть любой ресурс (файл, сокет и т.п.).
Всего доброго.
Re[3]: Выход из функции при ошибке
От: Stoune  
Дата: 27.12.02 01:54
Оценка: 2 (1)
Кажетса я гдето в MSDN видел об подобном статью

Я делаю так
try
{
    // здесь получаем ресурсы
    ptr = new char[256];
}catch(...)// или конкретный тип исключения
{
    if(ptr != null)delete ptr;
// Для других типов ресурсов аналогично    
}


Можно также использовать в более сложных ситуациях ещё и __try{} __finaly( Разширения от МелкоСофта ), но это только в крайних случаях и должно быть соответственно оправдано, у меня по крайней мере таких потребностей не возникало.
... << RSDN@Home 1.0 beta 4 >>
Re: Выход из функции при ошибке
От: WFrag США  
Дата: 27.12.02 03:07
Оценка:
Здравствуйте, menify, Вы писали:
А можно так ( без goto ):
MSG*    new_msg(..)
{
    MSG*        msg = NULL;
    HEADER*     header = NULL;
    ADDR*       to = NULL;
    ADDR*       from = NULL;
    char*       body = NULL;
    
    do
    {
        header = (HEADER*) malloc( sizeof(HEADER) );
        if (header == NULL)
            break;
    
        ...
    
        to = (ADDR*)malloc( sizeof(ADDR) );
        if (to == NULL)
            break;
    
        ...
    
        from = (ADDR*)malloc( sizeof(ADDR) );
        if (from == NULL)
            break;
    
        ...
    
        msg = (ADDR*)malloc( sizeof(MSG) );
        if (msg == NULL)
            break;
 
        ...
    
        return msg;
    } while( 0 );
    
    
    //-------------------------------------------------------//
    //          LABELS:                                      //
    if( body )
        free( body );                   // !!!
    if( from )
        free( from );                   // !!!
    if( to )
        free( to );                     // !!!
    if( header )
        free( header );                 // !!!
      return NULL;
}
Re[4]: Выход из функции при ошибке
От: menify Россия  
Дата: 27.12.02 03:23
Оценка:
Здравствуйте, Stoune:

try
{
    // здесь получаем ресурсы
    ptr = new char[256];
}catch(...)// или конкретный тип исключения
{
    if(ptr != null)delete ptr;
// Для других типов ресурсов аналогично    
}


А для CreateFile(..) или fopen(..) тоже будут генерироваться исключения?
Всего доброго.
Re[3]: Выход из функции при ошибке
От: vav Россия  
Дата: 27.12.02 03:26
Оценка: 3 (1)
Здравствуйте, menify, Вы писали:

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


vav>>А разве это не классический пример выгоды использования умных указателей?

M>Возможно. Я слабо представляю себе как их можно использовать?

M>Я говорю не только про выделение памяти, это может быть любой ресурс (файл, сокет и т.п.).


Концепция Smart Pointer
Re[5]: Выход из функции при ошибке
От: Mozhay Россия www.mozhay.chat.ru
Дата: 27.12.02 06:22
Оценка:
Здравствуйте, menify, Вы писали:

M>Здравствуйте, Stoune:


M>
M>try
M>{
M>    // здесь получаем ресурсы
M>    ptr = new char[256];
M>}catch(...)// или конкретный тип исключения
M>{
M>    if(ptr != null)delete ptr;
M>

Проверку на NULL можно убрать.
M>
M>// Для других типов ресурсов аналогично    
M>}
M>


M>А для CreateFile(..) или fopen(..) тоже будут генерироваться исключения?

M>
Можно проверить код возврата и в случае ошибки сгенерировать исключение.
При частом использовании таких ресурсов лучше сделать класс-обертку, в конструкторе которого выделяется ресурс (открывается файл), а в деструкторе — освобождается.
Хорошая статья на тему Resource management.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.