Выход из функции при ошибке
От: 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.
Re: Выход из функции при ошибке
От: WolfHound  
Дата: 27.12.02 06:55
Оценка:
Здравствуйте, menify, Вы писали:

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

Это велосипед но как иначе обяснить как это все работает.
template<class P_Type>
class T_Smart
{
    P_Type* m_ptr;
    //блокируем копирование
    T_Smart(const T_Smart& obj);
    T_Smart& operator=(const T_Smart& obj);
public:
    //захватываем готовый указатель
    //для вазова конструктора с параметрвми
    //T_Smart<Foo> foo=new Foo(p1, p2, p3);
    T_Smart(P_Type* ptr)
        :m_ptr(ptr)
    {}
    //создает новай обьект
    //конструктор по умолчанию
    T_Smart()
        :m_ptr(new P_Type)
    {}
    //разрушает обьект при выходе из области видимости
    ~T_Smart()
    {
        delete m_ptr;
    }
    //приведение к указателю на тип обьекта
    operator P_Type*()
    {
        return m_ptr;
    }
    //обращение к мемберам обьекта
    //foo->SomeFunc();
    P_Type* operator->()
    {
        return m_ptr;
    }
    //Отцепляет обьект например для возвращения из функции
    P_Type* Detach()
    {
        P_Type* t=m_ptr;
        m_ptr=0;
        return t;
    }
};

Для других ресурсов аналогично.
Будут вопросы спрашивай.
... << RSDN@Home 1.0 beta 4 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: Выход из функции при ошибке
От: jazzer Россия Skype: enerjazzer
Дата: 27.12.02 07:52
Оценка: 3 (1)
Здравствуйте, menify, Вы писали:

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


M>

ssm>>истина лежит в использовании, к примеру, std::auto_ptr
M>Я плохо знаю C++, что такое std::auto_ptr?
M>Как это будет выглядеть, хотя бы примерно?

это класс, который всасывает указателдь на что-нибудь (созданный при помощи new), а в деструкторе он его удаляет при помощи delete:
struct A
{
   int i;
   double j;
};
void f()
{
   std::auto_ptr<A> ap(new A); // создали
   ap->i = 7;                  // работаем, как с обычным указателем
   ap->j = 3.14;
}                              // а здесь объект ap выходит из области видимости и удаляется,
                               // что приводит к вызову деструктора, а в нем вызовется delete.
                               // то же самое произойдет при return или возбуждении исключения.

а то, что delete не делает ничего, если ему подсунуть нулевой указатель, то вообще можно быть спокойным и не бояться, что при такой автоматической очистке произойдет что-то не то.
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[4]: Выход из функции при ошибке
От: WolfHound  
Дата: 27.12.02 09:21
Оценка:
Здравствуйте, jazzer, Вы писали:

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

Я гдето слышал что изи этого оператора его в STL ные контейнеры совать нельзя
    _Ty& operator*() const _THROW0()
        {    // return designated value
        return (*get());
        }


В смысле будет падать
    std::vector<std::auto_ptr<Foo> > vec;


Можно так но у ATL::CAdapt такой дурацкий интерфейс что проще написать свой врапер и не мучаться.
    std::vector<ATL::CAdapt<std::auto_ptr<Foo> > > vec;
... << RSDN@Home 1.0 beta 4 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[5]: Выход из функции при ошибке
От: ssm Россия  
Дата: 27.12.02 09:28
Оценка:
Здравствуйте, WolfHound, Вы писали:

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


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

WH>Я гдето слышал что изи этого оператора его в STL ные контейнеры совать нельзя

так и есть, на самом деле комитет старается сделать все возможное, чтобы предотвратить использование auto_ptr в стандартных контейнерах, и именно для этого оператор присваивания и конструктор копирования принимают неконстантный объект

WH>Можно так но у ATL::CAdapt такой дурацкий интерфейс что проще написать свой врапер и не мучаться.

WH>
WH>    std::vector<ATL::CAdapt<std::auto_ptr<Foo> > > vec;
WH>


проще,наверное, будет использование интелектуальных указателей с подсчетом ссылок, например boost::smart_ptr, а не изобретение велосипеда. но суть дела в том, что у auto_ptr свои задачи, с которыми он отлично справляется
Re[6]: Выход из функции при ошибке
От: ssm Россия  
Дата: 27.12.02 09:44
Оценка:
Здравствуйте, ssm, Вы писали:

ssm>так и есть, на самом деле комитет старается сделать все возможное, чтобы предотвратить использование auto_ptr в стандартных контейнерах, и именно для этого оператор присваивания и конструктор копирования принимают неконстантный объект


прошу прощения, конечно же неконстантную ссылку на неконстантный объект:
Re[5]: Выход из функции при ошибке
От: Дмитрий Наумов  
Дата: 27.12.02 13:43
Оценка:
Здравствуйте, WolfHound, Вы писали:

auto_ptr нельзя использовать в контейнерах из-за того что при копировании\присваивании меняется ownership!

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


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

WH>Я гдето слышал что изи этого оператора его в STL ные контейнеры совать нельзя
WH>
WH>    _Ty& operator*() const _THROW0()
WH>        {    // return designated value
WH>        return (*get());
WH>        }
WH>


WH>В смысле будет падать

WH>
WH>    std::vector<std::auto_ptr<Foo> > vec;
WH>


WH>Можно так но у ATL::CAdapt такой дурацкий интерфейс что проще написать свой врапер и не мучаться.

WH>
WH>    std::vector<ATL::CAdapt<std::auto_ptr<Foo> > > vec;
WH>


WH>
... << RSDN@Home 1.0 beta 4 >>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.