try and catch, паранойя в использовании....
От: myaso  
Дата: 02.06.05 12:48
Оценка: :))) :)))
Всем привет!

У меня тут в последнее время появилось устойчивое желание использовать сабжевую конструкцию там где надо и не надо. Во всём нужно соблюдать золотую середину... Я её видать потерял чувство равновесия:( Просьба востановить! Хотя может быть я прав:)

И так. Например изначально каждую функцию я хочу оформлять вот так:
bool func()
{
    res = false;

    try
    {
        ......

        res = true;
    }
    catch (...)
    {
        res = false;
    }

    return res;
}


далее из-за боязни всего что движеться и не движеться там где я выделяю память я хочу делать вот так
bool func()
{
    res = false;

    try
    {
        void* pTmp = malloc(size);
        if (pTmp != NULL)
        {
            try
            {
                ...........

                res = true;
            }
            catch (...)
            {
                res = false;
            }

            free(pTmp);

        }        
    }
    catch (...)
    {
        res = false;
    }

    return res;
}


соотвественно если мне нужно пару раз выделить память. я и там прикручу сабж

bool func()
{
    res = false;

    try
    {
        void* pTmp1 = malloc(size);
        if (pTmp1 != NULL)
        {
            try
            {
                void* pTmp2 = malloc(size);
                if (pTmp2 != NULL)
                {
                    try
                    {
                        ...........

                        res = true;
                    }
                    catch (...)
                    {
                        res = false;
                    }

                    free(pTmp2);

                }

                res = true;
            }
            catch (...)
            {
                res = false;
            }

            free(pTmp1);

        }        
    }
    catch (...)
    {
        res = false;
    }

    return res;
}


а ещё понравилось делать вот так

bool func()
{
    res = false;

    try
    {
        тут что-то делается. потом проверяется условие если фолс то делаем throw
        if (condition == false)
        {
            throw -1;
        }
        иначе продолжаем работу....

        потом ситуация может повториться
        if (condition == false)
        {
            throw -1;
        }

    }
    catch (...)
    {
        res = false;
    }

    return res;
}



Что скажите? Говорить о том что например тот же malloc или любая функция которуюя хочу дёрнуть не кидает експешин меня никоим образом не успокаивает. Я из-за недоверия ей всё равно хочу взять её в try, не смотря ни на что, даже ни смотря на более медленную работу. Вот такие пироги.

PS наверное хотелось бы для начала поговорить о С++, поэтому SEH с её __finally не стоит рассматривать.
Re: try and catch, паранойя в использовании....
От: Владик Россия  
Дата: 02.06.05 13:15
Оценка: +3
Здравствуйте, myaso, Вы писали:

M>PS наверное хотелось бы для начала поговорить о С++,


Начни для начала с изучения RAII в C++.
Как все запущенно...
Re: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 02.06.05 14:58
Оценка:
Здравствуйте, myaso, Вы писали:

Сначало смотрим про обертки для С++ динамических указателей и поведение объектов в стеке. Потом смотрим чем обходятся исключения в плане производительности. И получается такая шняга:
1. Вся логика программы свободно может прожить без исключений.
2. Исключениями пользуются только для исключительных случаев. (например неожиданные ошибки).

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[2]: try and catch, паранойя в использовании....
От: myaso  
Дата: 02.06.05 15:10
Оценка:
мг... нашёл... сейчас поизучаем...
Re: try and catch, паранойя в использовании....
От: Igor Trofimov  
Дата: 02.06.05 16:28
Оценка: 1 (1) +1
M>И так. Например изначально каждую функцию я хочу оформлять вот так:
M>
M>bool func()
M>{
M>    res = false;

M>    try
M>    {
M>        ......

M>        res = true;
M>    }
M>    catch (...)
M>    {
M>        res = false;
M>    }

M>    return res;
M>}


А зачем в среде, где есть механизм структурированной обработки исключений использовать булевский признак успешного завершения для каждой функции???
Re: try and catch, паранойя в использовании....
От: Severn Россия  
Дата: 03.06.05 03:05
Оценка: +1
Здравствуйте, myaso, Вы писали:


M>PS наверное хотелось бы для начала поговорить о С++, поэтому SEH с её __finally не стоит рассматривать.


Дык это у тебя чистейшей воды С + коды возврата, а не С++ + исключения
Re: try and catch, паранойя в использовании....
От: Дарней Россия  
Дата: 03.06.05 04:10
Оценка: +1
Здравствуйте, myaso, Вы писали:

M>И так. Например изначально каждую функцию я хочу оформлять вот так:

M>
M>bool func()
M>{
M>    res = false;

M>    try
M>    {
M>        ......

M>        res = true;
M>    }
M>    catch (...)
M>    {
M>        res = false;
M>    }

M>    return res;
M>}


это не паранойя, это чистейшей воды "возвращение в пещеры" с заменой исключений на коды возврата
Всех излечит, исцелит
добрый Ctrl+Alt+Delete
Re: try and catch, паранойя в использовании....
От: mihoshi Россия  
Дата: 03.06.05 06:05
Оценка: -3
Здравствуйте, myaso, Вы писали:

try-catch для того и нужен (в частности), чтобы глубокой вложенности избежать.
То, что ты нарисовал, делается примерно так

#define assert(x) {if (!x) {throw -1;}}

void func()
{
    void *pTmp1=0, *pTmp2=0;
    try
    {
       pTmp1 = malloc(size);
       assert(pTmp1 != 0);  
       pTmp2 = malloc(size);
       assert(pTmp2 != 0);  
       ...........
    } catch (...) {
       if(pTmp1 != 0) 
         free (pTmp1);
       if(pTmp2 != 0) 
         free (pTmp2);
       throw; 
    }
}


Хотя лучше, конечно, использовать специальный тип исключения, а не int.
Re: try and catch, паранойя в использовании....
От: Eugene Kilachkoff Россия  
Дата: 03.06.05 07:08
Оценка:
Здравствуйте, myaso, Вы писали:

M>У меня тут в последнее время появилось устойчивое желание использовать сабжевую конструкцию там где надо и не надо. Во всём нужно соблюдать золотую середину... Я её видать потерял чувство равновесия Просьба востановить! Хотя может быть я прав

Ну, во первых, ты смешал здесь два способа обработки ошибок. Исторически их вообще было два: коды возврата и прокидывание их вручную по всему уровням, и, собственно, поддерживаемые компилятором и runtime исключения. Сильно упрощая можно сказать, что компилятор с исключениями генерит примерно такой же код, что и в первом варианте, но только автоматически.

M>Что скажите? Говорить о том что например тот же malloc или любая функция которуюя хочу дёрнуть не кидает експешин меня никоим образом не успокаивает. Я из-за недоверия ей всё равно хочу взять её в try, не смотря ни на что, даже ни смотря на более медленную работу. Вот такие пироги.

Во вторых, код, подобный скипнутому нужен только в одном случае — при написании "исключительной" обертки к API, которая не поддерживает исключения, или наоборот, обработка ошибок в коде, где исключений не должно быть, а нижележащий API их выкидывает.
Вообще, предпочитаемый метод обработки определяется стилем имеющегося на данный момент кода, а также здравым смыслом.
Re[2]: try and catch, паранойя в использовании....
От: myaso  
Дата: 03.06.05 07:10
Оценка:
Всё что я могу сказать в своё оправдание, так это то что приходиться учиться по ходу(ну наверное так у всех), а время оно ограниченно:( Спасибо за коменты :)
Re[2]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 03.06.05 12:55
Оценка:
Здравствуйте, mihoshi, Вы писали:

Заделать шаблончик для динамического объекта — 5 минут ручной работы (если стандартный не хочешь использовать). И тогда нафиг этот catch сдался. Лишняя писанина(да к тому-же опасная).

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[2]: try and catch, паранойя в использовании....
От: ansi  
Дата: 03.06.05 23:51
Оценка: 6 (1)
Здравствуйте, mihoshi, Вы писали:

M>
M>// Товарисч, букву хэ надо заключать в скобки
M>#define assert(x) {if (!(x)) {throw -1;}}

M>void func()
M>{
M>    void *pTmp1=0, *pTmp2=0;
M>    try
M>    {
M>       pTmp1 = malloc(size);
M>       assert(pTmp1 != 0);  
M>       pTmp2 = malloc(size);
M>       assert(pTmp2 != 0);  
M>       ...........
M>    } catch (...) {
M>       if(pTmp1 != 0) 
M>         free (pTmp1);
M>       if(pTmp2 != 0) 
M>         free (pTmp2);
M>       throw; 
M>    }
M>}
M>


M>Хотя лучше, конечно, использовать специальный тип исключения, а не int.
new RSDN@Home(1.1.4, 303) << new Message(); std::head::ear << "Celtic Angels — Angels Sea";
Re: try and catch, паранойя в использовании....
От: IT Россия linq2db.com
Дата: 04.06.05 03:23
Оценка: :))) :))
Здравствуйте, myaso, Вы писали:

M>И так. Например изначально каждую функцию я хочу оформлять вот так:


Срочно к доктору. Сначала к хирургу по кодам возврата (пусть это всё отрежет нафиг), потом к терапевту по исключениям.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: try and catch, паранойя в использовании....
От: myaso  
Дата: 05.06.05 13:26
Оценка:
Здравствуйте, IT, Вы писали:

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


M>>И так. Например изначально каждую функцию я хочу оформлять вот так:


IT>Срочно к доктору. Сначала к хирургу по кодам возврата (пусть это всё отрежет нафиг), потом к терапевту по исключениям.


ага... всё таки исключения есть "рулез" ? :) а как же вот это мнение?

GZ>Сначало смотрим про обертки для С++ динамических указателей и поведение объектов в стеке. Потом смотрим чем обходятся исключения в плане производительности. И получается такая шняга:

GZ>1. Вся логика программы свободно может прожить без исключений.
GZ>2. Исключениями пользуются только для исключительных случаев. (например неожиданные ошибки).
Re: try and catch, паранойя в использовании....
От: RST_Angellab  
Дата: 05.06.05 13:38
Оценка:
Исключения не всегда хорошо.
1) Исключения в деструкторе к примеру.
2) Раскрутка стека не всегда корректно отрабатывает — деструкторы могут не вызываться — были случаи, не помню конкретных примеров
3) Исключения приводят к работе ведра. Соответсвенно любой throw (-1) >= 2000 тактов процессора. Если это происходит довольно часто — даже на хороших машинах тормоза ощутимы.
Re[3]: try and catch, паранойя в использовании....
От: IT Россия linq2db.com
Дата: 05.06.05 16:08
Оценка:
Здравствуйте, myaso, Вы писали:

IT>>Срочно к доктору. Сначала к хирургу по кодам возврата (пусть это всё отрежет нафиг), потом к терапевту по исключениям.


M>ага... всё таки исключения есть "рулез" ? а как же вот это мнение?


Нормальное мнение. Использование исключений для программирования логики — это неправильно. Для исключительных ситуаций — самое оно.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: try and catch, паранойя в использовании....
От: mihoshi Россия  
Дата: 06.06.05 05:11
Оценка:
Здравствуйте, GlebZ, Вы писали:

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


GZ>Заделать шаблончик для динамического объекта — 5 минут ручной работы (если стандартный не хочешь использовать). И тогда нафиг этот catch сдался. Лишняя писанина(да к тому-же опасная).


Я писал пример того, как сделать близко к тексту изначального примера. Разумеется, личныо я сырые malloc-и в работе я не использую. Но если уж товарищу приспичило...
Re[4]: try and catch, паранойя в использовании....
От: Mika Soukhov Stock#
Дата: 06.06.05 10:00
Оценка: :)
Здравствуйте, IT, Вы писали:

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


IT>>>Срочно к доктору. Сначала к хирургу по кодам возврата (пусть это всё отрежет нафиг), потом к терапевту по исключениям.


M>>ага... всё таки исключения есть "рулез" ? а как же вот это мнение?


IT>Нормальное мнение. Использование исключений для программирования логики — это неправильно. Для исключительных ситуаций — самое оно.


Это вообще в корне неверно. На основе ошибок работают такие вещи, как: MSMQ (в зависимости от исключений, доставляются данные или нет), компиляторы (продолжается компиляция или все останавливается на этапе разбора текста) или система предупреждения атаки со стороны вредоносных систем (аудиторские системы проводят мониторинг данных, и, если хоть один их элементов не отвечает, то он закрывается от внешнего доступа).
Re[4]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 06.06.05 10:36
Оценка:
Здравствуйте, mihoshi, Вы писали:

M>Я писал пример того, как сделать близко к тексту изначального примера. Разумеется, личныо я сырые malloc-и в работе я не использую. Но если уж товарищу приспичило...

Товарсчь пока на полпути к нирване и помочь ему достигнуть ее — дело каждого пионэра любящего партию и народ. Если не возражаешь, перепишу твой код. А так как у меня и настроение хорошее, даже не буду пользоваться всякими стлными штучками(чтобы показать что все просто).

//хотя есть системный assert, возьмем твой
#define assert(x) {if (!(x)) {throw -1;}}

//заделали свой шаблончик
template <class T>
class my_ptr
{
public:
    T* m_t;
    //здесь вместо malloc используем new. Это повысит переносимость.
    my_ptr(int size){m_t=0;m_t=new T[size];assert(m_t!=0);};
    ~my_ptr(){delete m_t;m_t=0;};
    T* operator->(){return m_t;};
    T operator*(){return *m_t;};
}; 
//и теперь все стало очень просто
//поскольку невыделение памяти - ошибка фатальная
//то лечится только остановкой программы
void func()
{
    my_ptr<char> pTmp1(100);
    my_ptr<char> pTmp2(100);
}



С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[5]: try and catch, паранойя в использовании....
От: myaso  
Дата: 06.06.05 12:12
Оценка:
Здравствуйте, GlebZ, Вы писали:

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


M>>Я писал пример того, как сделать близко к тексту изначального примера. Разумеется, личныо я сырые malloc-и в работе я не использую. Но если уж товарищу приспичило...

GZ>Товарсчь пока на полпути к нирване и помочь ему достигнуть ее — дело каждого пионэра любящего партию и народ. Если не возражаешь, перепишу твой код. А так как у меня и настроение хорошее, даже не буду пользоваться всякими стлными штучками(чтобы показать что все просто).

GZ>
GZ>//хотя есть системный assert, возьмем твой
GZ>#define assert(x) {if (!(x)) {throw -1;}}

GZ>//заделали свой шаблончик
GZ>template <class T>
GZ>class my_ptr
GZ>{
GZ>public:
GZ>    T* m_t;
GZ>    //здесь вместо malloc используем new. Это повысит переносимость.
GZ>    my_ptr(int size){m_t=0;m_t=new T[size];assert(m_t!=0);};
GZ>    ~my_ptr(){delete m_t;m_t=0;};
GZ>    T* operator->(){return m_t;};
GZ>    T operator*(){return *m_t;};
GZ>}; 
GZ>//и теперь все стало очень просто
GZ>//поскольку невыделение памяти - ошибка фатальная
GZ>//то лечится только остановкой программы
GZ>void func()
GZ>{
GZ>    my_ptr<char> pTmp1(100);
GZ>    my_ptr<char> pTmp2(100);
GZ>}
GZ>


мм спасибо за помосчь! нирвана близка:)
мысли вслух: полностью соглашаюсь с тем что "невыделение памяти — ошибка фатальная"(и вообще со всем соглашусь), но то что она "лечится только остановкой программы" как-то неочень.....
Re[6]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 06.06.05 12:29
Оценка:
Здравствуйте, myaso, Вы писали:

M>мысли вслух: полностью соглашаюсь с тем что "невыделение памяти — ошибка фатальная"(и вообще со всем соглашусь), но то что она "лечится только остановкой программы" как-то неочень.....

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

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[7]: try and catch, паранойя в использовании....
От: Cyberax Марс  
Дата: 06.06.05 12:55
Оценка:
GlebZ wrote:

> M>мысли вслух: полностью соглашаюсь с тем что "невыделение памяти —

> ошибка фатальная"(и вообще со всем соглашусь), но то что она "лечится
> только остановкой программы" как-то неочень.....
> Безусловно, сильно зависит от окружения (на чем выполняется
> программа). Если это случилось для Win32 — это означает туши свет.
> Единственные причины могут быть в том, что обрушилась система
> управлением памятью, или ее нехватка.

Еще причиной может быть кастомный аллокатор (например, для расшареной
памяти), у которого закончилась свободная память.

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[8]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 06.06.05 13:08
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Еще причиной может быть кастомный аллокатор (например, для расшареной

C>памяти), у которого закончилась свободная память.
Каждому кастомному аллокатору свои кастомные правила. Там уже и аля std:exception из аллокатора не грех.

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[9]: try and catch, паранойя в использовании....
От: Cyberax Марс  
Дата: 06.06.05 14:40
Оценка:
GlebZ wrote:

> C>Еще причиной может быть кастомный аллокатор (например, для расшареной

> C>памяти), у которого закончилась свободная память.
> Каждому кастомному аллокатору свои кастомные правила. Там уже и аля
> std:exception из аллокатора не грех.

Стандартом, вообще говоря, предусмотрен std::bad_alloc, который может
вылететь и из стандартного new. Если это не нужно, то следует
использовать new(std::nothow).

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[10]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 06.06.05 14:59
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Стандартом, вообще говоря, предусмотрен std::bad_alloc, который может

C>вылететь и из стандартного new. Если это не нужно, то следует
C>использовать new(std::nothow).
+1. Просто упомянул именно std::exception которые родитель всея std исключений. Тогда можно возвращать более информативные ошибки.

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[5]: try and catch, паранойя в использовании....
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.06.05 06:13
Оценка: 1 (1)
Здравствуйте, GlebZ, Вы писали:
GZ>Товарсчь пока на полпути к нирване и помочь ему достигнуть ее — дело каждого пионэра любящего партию и народ. Если не возражаешь, перепишу твой код. А так как у меня и настроение хорошее, даже не буду пользоваться всякими стлными штучками(чтобы показать что все просто).
А-я-яй!
//хотя есть системный assert, возьмем твой
#define assert(x) {if (!(x)) {throw -1;}}

//заделали свой шаблончик
template <class T>
class my_ptr
{
public:
    T* m_t;
    //здесь вместо malloc используем new. Это повысит переносимость.
    my_ptr(int size){m_t=0;m_t=new T[size];assert(m_t!=0);};
    ~my_ptr(){delete[] m_t; m_t=0;}; // для типов с нетривиальным деструктором необходимо корректное убиение!
    T* operator->(){return m_t;};
    T operator*(){return *m_t;};
}; 
//и теперь все стало очень просто
//поскольку невыделение памяти - ошибка фатальная
//то лечится только остановкой программы
void func()
{
    my_ptr<char> pTmp1(100);
    my_ptr<char> pTmp2(100);
}
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 07.06.05 07:03
Оценка:
Здравствуйте, Sinclair, Вы писали:

Thanks за замечание.
Код который никогда не запускался, и никогда не выдавал ошибок, не считается багнутым

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re: try and catch, паранойя в использовании....
От: faulx  
Дата: 07.06.05 09:00
Оценка: 1 (1)
Возможно, поднятая в заголовке сообщения проблема сложнее, чем кажется некоторым из ответивших на него. Для начала приведу некоторые ссылки

1. В этой статье в конце приводятся некоторые соображения по поводу исключений.

2. Из предыдущей статьи есть ссылка на другой небезынтересный материал.

Теперь немного своими словами. С одной стороны мы все знаем, что исключения позволяют разделить "нормальный" код и код для обработки ошибок. Но затруднения связаны с уровнем, на котором необходимо производить эту обработку.

Скажем, декларируется, что в .NET все извещения об ошибках осуществляются с помощью исключений, которые все в конечном итоге порождениы от класса System.Exception. (кажется, это не совсем так, но в данном случае на тонкости можно не обращать внимания) Однако перехватывать исключения этого базового класса категорически не рекомендуется (Рихтер в своей книге достаточно убедительно обосновывает, почему). Следовательно, если вызывается некоторый метод некоторого объекта, необходимо выяснить, какие исключения может выбрасывать этот метод и проводить раздельную обработку каждого исключения (если, конечно, по какой-то счастливой случайности они все не имеют одного общего потомка):

void OuterMethod()
{
  try
  {
    obj.InnerMethod(); // Может выбрасывать исключения классов Ex1, Ex2, Ex3 
  }
  catch(Ex1 e)
  {
   processError(e);
  }
  catch(Ex2 e)
  {
   processError(e);
  }
  catch(Ex3 e)
  {
   processError(e);
  }
}


Заметим, что здесь обработка ошибок совершенно идентична для всех трех вариантов. Могут возразить, что эта обработка должна происходить на более высоком уровне, чуть ли не в районе метода Main(). Но это не решает проблемы. Если только один метод может выбрасывать несколько исключений, какое же количество исключений может выбросить вся программа, состоящая из вызовов множества методов! Блоки catch в методе Main() разрастутся до совершенно невероятных размеров. (На практике, как я подозреваю, большинство программистов сдается и все-таки перехватывает System.Exception. Возможные последствия этого достаточно красочно описаны у Рихтера).

Другой недостаток переноса обработки исключений на слишком высокий уровень программы — потеря контекста, в котором произошло исключение. Печально известное исключение NullReferenceException — только один, самый популярный пример. О чем говорит это исключене, пойманное в Main()? Практически ни о чем! Оно может произойти по стольким причинам, что для диагностирования ситуации поможет не больше, чем окошко Access Violation.

Справедливости ради надо сказать, что некоторую помощь окажет, безусловно, stack trace. Однако надо понимать, что stack trace — это инструмент, помогающий только программисту, написавшему программу. Конечный пользователь никакой значимой пользы из него не извлечет, т.е. не поймет, что нужно исправить ему, чтобы сообщение об ошибке перестало появляться.

Решением этой проблемы для пользователя может служить не иерархия вызовов методов (чем и является stack trace), а иерархия выбрасываемых исключений. Для этого при проектировании исключений в .NET была предусмотрена возможность при создании исключения указать другое исключение, послужившее причиной этого. Вот это действительно удобно, поскольку позволяет "протянуть" информацию, доступную только в самом низу иерархии вызовов до того места, где исключение перехватывается, дополняя ее по пути другой информацией.

Например (пример намеренно упрощен и, возможно, не требует таких сложностей), если при инициализации какого-то объекта необходимо выполнить ряд сложных действий (открыть несколько файлов, прочитать их содержимое и т.п.), получение на верхнем уровне исключения с сообщением вроде "Ошибка при разборе строки 'xxx'" (это исключение выбрасывает повторно используемый метод для разбора строки из файла конфигурации, когда этот разбор не может быть осуществлен) заставляет гадать — при разборе какого именно файла произошла ошибка. Если же это исключение будет перехвачено методом, в котором известно имя файла и в качестве реакции будет брошено новое исключение, сообщение об ошибке будет более информативным: "Ошибка при разборе файла 'badfile.ini', причина: "Ошибка при разборе строки 'xxx'"", то мы находимся в более выгодном положении и по-крайней мере знаем место локализации ошибки. Если же добавить еще несколько уровней в иерархии вызовов, различия становятся еще более разительными.

Но при этом теряется то преимущество исключений, что их обработку можно проводить на более высоком уровне! Возвращаясь к приведенному почти в начале этого сообщения коду, можно заключить, что перехват и обработку исключений, бросаемых методом InnerMethod(), нельзя выносить за пределы OuterMethod(). Вместо этого надо сообщить в документации, что OuterMethod() в случае ошибки выбрасывает исключение OuterMethodException, а processError() реализовать как

void processError(Exception e)
{
  throw OuterMethodException("OuterMethod() failed", e);
}


При таком подходе код становится не так уж отличим от кода с явной проверкой возвращаемых значений и кодов ошибок. Но такова, видимо, цена, которую необходимо платить за то, чтобы программа сообщала действительно полезные сведения в случае ошибки.

PS. Была упомянута проблема потери контекста в случае слишком высокого по иерархии перехвата исключений. В связи с этим нельзя не вспомнить подход, применяемый в Common Lisp: Соответствующая глава из Practical Common Lisp.
Re[5]: try and catch, паранойя в использовании....
От: dshe  
Дата: 07.06.05 20:00
Оценка:
Здравствуйте, GlebZ, Вы писали:

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


GZ>
GZ>#define assert(x) {if (!(x)) {throw -1;}}
GZ>


Не совсем по теме, но мне кажется, что выражать assert, через исключения не совсем корректно. Если утвержение в assert не сработало (т.е. ложно), то программа должна вывести информацию о том, где произошла ошибка (имя файла, номер строки), и свалиться. Свалиться желательно с coredump'ом, по которому потом можно будет восстановить состояние программы в момент возникновения ошибки.

Если в assert'е выбрасываются исключение (независимо от того, -1 или какого-то своего класса), то существует риск того, что это исключение будет как-то отработано (при помощи catch(...)) и информация об ошибке (и о состоянии программы в момент ошибки), будет безвозвратно утеряна.
--
Дмитро
Re[5]: try and catch, паранойя в использовании....
От: McSeem2 США http://www.antigrain.com
Дата: 08.06.05 02:23
Оценка: 1 (1) +1
Здравствуйте, Mika Soukhov, Вы писали:

MS>Это вообще в корне неверно. На основе ошибок работают такие вещи, как: MSMQ (в зависимости от исключений, доставляются данные или нет), компиляторы (продолжается компиляция или все останавливается на этапе разбора текста) или система предупреждения атаки со стороны вредоносных систем (аудиторские системы проводят мониторинг данных, и, если хоть один их элементов не отвечает, то он закрывается от внешнего доступа).


Один мой знакомый писал конечный автомат для какого-то навороченного voice mail на основе исключений. То есть, у него там события передавались при помощи исключений. Получилось очень компактно и работало очень надежно. Ну а быстродействие в таком автомате — вещь не сильно нужная.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[6]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 08.06.05 09:24
Оценка:
Здравствуйте, dshe, Вы писали:

D>Не совсем по теме, но мне кажется, что выражать assert, через исключения не совсем корректно. Если утвержение в assert не сработало (т.е. ложно), то программа должна вывести информацию о том, где произошла ошибка (имя файла, номер строки), и свалиться. Свалиться желательно с coredump'ом, по которому потом можно будет восстановить состояние программы в момент возникновения ошибки.

При подобной ошибке, когда выделение динамической памяти невозможно, дополнительные действия только усугубляют ситуацию.

D>Если в assert'е выбрасываются исключение (независимо от того, -1 или какого-то своего класса), то существует риск того, что это исключение будет как-то отработано (при помощи catch(...)) и информация об ошибке (и о состоянии программы в момент ошибки), будет безвозвратно утеряна.

Почти да. Рискну сделать два предположения:
1. catch(...) — это плохо. Потому как никогда не знаешь откуда какая ошибка может возникнуть. Можно только предполагать. Следовательно, нужно обрабатывать только те ошибки, о которых имеешь предстваление.
2. в данном случае, мы выполняем assert. Ну а assert используется только в отладочных целях. Взамен лучше ставить конечно явное исключение.
3. Конечно, предложенный Cyberax std::bad_alloc
Автор: Cyberax
Дата: 06.06.05
лучше.

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[6]: try and catch, паранойя в использовании....
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 08.06.05 11:50
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Здравствуйте, Mika Soukhov, Вы писали:


MS>>Это вообще в корне неверно. На основе ошибок работают такие вещи, как: MSMQ (в зависимости от исключений, доставляются данные или нет), компиляторы (продолжается компиляция или все останавливается на этапе разбора текста) или система предупреждения атаки со стороны вредоносных систем (аудиторские системы проводят мониторинг данных, и, если хоть один их элементов не отвечает, то он закрывается от внешнего доступа).


MS>Один мой знакомый писал конечный автомат для какого-то навороченного voice mail на основе исключений. То есть, у него там события передавались при помощи исключений. Получилось очень компактно и работало очень надежно. Ну а быстродействие в таком автомате — вещь не сильно нужная.


+1

AFAIK, сам Страуструп в книге "Язык программирования C++" описывал возможность применения исключений подобным образом.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[7]: try and catch, паранойя в использовании....
От: stalcer Россия  
Дата: 08.06.05 12:39
Оценка: +1
Здравствуйте, GlebZ, Вы писали:

GZ>1. catch(...) — это плохо. Потому как никогда не знаешь откуда какая ошибка может возникнуть. Можно только предполагать. Следовательно, нужно обрабатывать только те ошибки, о которых имеешь предстваление.


Плохо не catch(...), а гашение исключений при помощи catch(...). Если catch(...) исплоьзуется так:

try
{
    SomeFunc(...);
} 
catch (...)
{
    // Do nothing. No rethrow.
};

то это, безусловно, плохо.

Но иногда я ловлю все исключения на границе какой-либо подсистемы, например, так:

try
{
    doCompile();
}
catch (const CompileException &e)
{
    throw; // Just rethrow.
}
catch (const std::bad_alloc &e)
{
    throw CompileException("Not enought memory for compilation.");
}
catch (...)
{
    throw CompileException("Internal compilation error.");
}

Не вижу в этом ничего плохого.
http://www.lmdinnovative.com (LMD Design Pack)
Re[7]: try and catch, паранойя в использовании....
От: mihoshi Россия  
Дата: 08.06.05 12:46
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>1. catch(...) — это плохо. Потому как никогда не знаешь откуда какая ошибка может возникнуть. Можно только предполагать. Следовательно, нужно обрабатывать только те ошибки, о которых имеешь предстваление.


Он используется исключительно чтобы освободить выделенные ресурсы и сделать throw дальше.
Re[6]: try and catch, паранойя в использовании....
От: mihoshi Россия  
Дата: 08.06.05 12:49
Оценка:
Здравствуйте, dshe, Вы писали:

GZ>>
GZ>>#define assert(x) {if (!(x)) {throw -1;}}
GZ>>


D>Не совсем по теме, но мне кажется, что выражать assert, через исключения не совсем корректно. Если утвержение в assert не сработало (т.е. ложно), то программа должна вывести информацию о том, где произошла ошибка (имя файла, номер строки), и свалиться. Свалиться желательно с coredump'ом, по которому потом можно будет восстановить состояние программы в момент возникновения ошибки.


Если я переименую assert в verify, это поможет?
Re[8]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 08.06.05 12:59
Оценка:
Здравствуйте, stalcer, Вы писали:

Насчет rethrow согласен. Работаю на нескольких языках, в большинстве случаев finally хватает. Сорри. Сначала написал, а потом подумал об этом случае, но он вполне понятен и не очень связан с предыдущей ситуацией.

S>Но иногда я ловлю все исключения на границе какой-либо подсистемы, например, так:


S>
S>try
S>{
S>    doCompile();
S>}
S>catch (const CompileException &e)
S>{
S>    throw; // Just rethrow.
S>}
S>catch (const std::bad_alloc &e)
S>{
S>    throw CompileException("Not enought memory for compilation.");
S>}
S>catch (...)
S>{
S>    throw CompileException("Internal compilation error.");
S>}
S>

S>Не вижу в этом ничего плохого.
Конечно все зависит от конкретной ситуацией. Если это обозначает полное обновление состояние данной подсистемы после поимки исключения, то это нормально. Если состояние неубивается то адекватность дальнейшей работы уже непонятна.

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[7]: try and catch, паранойя в использовании....
От: dshe  
Дата: 08.06.05 20:03
Оценка: 4 (2) +1
Здравствуйте, mihoshi, Вы писали:

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


GZ>>>
GZ>>>#define assert(x) {if (!(x)) {throw -1;}}
GZ>>>


D>>Не совсем по теме, но мне кажется, что выражать assert, через исключения не совсем корректно. Если утвержение в assert не сработало (т.е. ложно), то программа должна вывести информацию о том, где произошла ошибка (имя файла, номер строки), и свалиться. Свалиться желательно с coredump'ом, по которому потом можно будет восстановить состояние программы в момент возникновения ошибки.


M>Если я переименую assert в verify, это поможет?


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

В одном из проектов, в котором я учавствовал, assert бросал исключения. Эти исключения потом на самом верхнем уровне ловились и в лог писалось что-то вроде "Internal Error", "Unknown Error", "Something wrong" или "Shit happened". Пользы от подобных сообщений было -- ноль! Зато программа делала вид, что работает.
--
Дмитро
Re[9]: try and catch, паранойя в использовании....
От: IT Россия linq2db.com
Дата: 09.06.05 02:07
Оценка:
Здравствуйте, GlebZ, Вы писали:

S>>Но иногда я ловлю все исключения на границе какой-либо подсистемы, например, так:


А в чём поинт? Почему не написать просто так:

try
{
    doCompile();
}
catch (const std::bad_alloc &e)
{
    throw CompileException("Not enought memory for compilation.");
}
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: try and catch, паранойя в использовании....
От: stalcer Россия  
Дата: 09.06.05 07:22
Оценка: 2 (2) +1
Здравствуйте, IT, Вы писали:

IT>А в чём поинт? Почему не написать просто так:


IT>
IT>try
IT>{
IT>    doCompile();
IT>}
IT>catch (const std::bad_alloc &e)
IT>{
IT>    throw CompileException("Not enought memory for compilation.");
IT>}
IT>


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

И потом, фактически у тебя нет шанса/смысла обработать левые исключения, выброшенные из этой подсистемы, потому как в соответствии с инкапсуляцией ты не знаешь как она устроена и почему именно это исключение возникло. А даже если и знаешь, то это исключение означает лишь одно, — что при компиляции произошол сбой, которого быть не должно и процесс компиляции прекращен.
http://www.lmdinnovative.com (LMD Design Pack)
Re[10]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 09.06.05 08:00
Оценка:
Здравствуйте, IT, Вы писали:

IT>А в чём поинт? Почему не написать просто так:

IT>
IT>try
IT>{
IT>    doCompile();
IT>}
IT>catch (const std::bad_alloc &e)
IT>{
IT>    throw CompileException("Not enought memory for compilation.");
IT>}
IT>

Можешь. Но в данном случае разговор о catch(...).

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[11]: try and catch, паранойя в использовании....
От: IT Россия linq2db.com
Дата: 09.06.05 15:37
Оценка: 1 (1) +1
Здравствуйте, stalcer, Вы писали:

S>catch (...) нужен для того, что если даже в подсистеме компиляции глюк, и она выбрасывает какое-либо левое исключение, то это не приведет к падению, например, всего сервера приложений, в котором она используется. Это позволяет четко специфицировать, что данная подсистема выбрасывает только CompileException и ничего больше. Так будет более удобно работать с ней на более высоких уровнях приложения.


Как минимум в данном примере мы теряем первопричину исключения. От того, что оно теперь какого-то одного типа ни жарко ни холодно. Всегда можно (и нужно) поставить на самом высоком уровне catch(...), чтобы избежать падений сервера приложения. Но давить первопричину исключения — это ИМХО даже хуже чем вообще его не обрабатывать.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[12]: try and catch, паранойя в использовании....
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 10.06.05 02:51
Оценка:
Здравствуйте, IT, Вы писали:

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


S>>catch (...) нужен для того, что если даже в подсистеме компиляции глюк, и она выбрасывает какое-либо левое исключение, то это не приведет к падению, например, всего сервера приложений, в котором она используется. Это позволяет четко специфицировать, что данная подсистема выбрасывает только CompileException и ничего больше. Так будет более удобно работать с ней на более высоких уровнях приложения.


IT>Как минимум в данном примере мы теряем первопричину исключения. От того, что оно теперь какого-то одного типа ни жарко ни холодно. Всегда можно (и нужно) поставить на самом высоком уровне catch(...), чтобы избежать падений сервера приложения. Но давить первопричину исключения — это ИМХО даже хуже чем вообще его не обрабатывать.


С теоритической точки зрения все это так. Но в C++ проблема, зачастую, бывает в использовании сторонней библиотеки, которая использует исключения, не производные от std::exception. И еще хорошо, если есть своя вершина иерархии классов исключений, чтобы можно было писать так:
try
    {
        ...
    }
catch( const std::exception & x )
    {
        ...
    }
catch( const some_another_exception_root & x )
    {
        ...
    }


Хуже, если таких some_another_exception_root-ов несколько. В таких сутациях catch(...), имхо, вполне допустим.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[13]: try and catch, паранойя в использовании....
От: IT Россия linq2db.com
Дата: 10.06.05 03:07
Оценка:
Здравствуйте, eao197, Вы писали:

E>Хуже, если таких some_another_exception_root-ов несколько. В таких сутациях catch(...), имхо, вполне допустим.


Понятное дело допустим. Я о том, что перехват всех подряд исключений и замена их на свои — это не лучший вариант. Даже в .NET, где для этой цели есть InnerException. А уж в плюсах и подавно. В RFD я поддался на уговоры и сделал так же. Т.е. из RFD выходят только исключения RFD. Ничего хорошего из этого не вышло. Практической пользы мизер, неудобств масса.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[12]: try and catch, паранойя в использовании....
От: stalcer Россия  
Дата: 10.06.05 05:50
Оценка:
Здравствуйте, IT, Вы писали:

IT>Как минимум в данном примере мы теряем первопричину исключения.


Первопричина таких исключений одна — ты где-то налажал в коде. Это на самом деле и есть логическая причина, т.е. причина, интересная пользователю данной подсистемы. Ее и надо показывать.

А если рассуждать по твоему, то следующий код тоже будет неправильным:

SQLStmt stmt = Conn.PrepareStmt("insert into UserTable values (:p0)");

try
{
    stmt.setStringParam(1, userName);
    stmt.execute();
}
catch (const SQLUniqueConstraintException &e)
{
    throw MyException("User already exist.");
}

В этом коде мы прячем первопричину, точно также, как и случае с моим предыдущим примером.


IT>От того, что оно теперь какого-то одного типа ни жарко ни холодно. Всегда можно (и нужно) поставить на самом высоком уровне catch(...), чтобы избежать падений сервера приложения.


От того, что подсистема выкидывает левые, неспецифицированные в ней исключения, становится очень "холодно". catch(...) на самом высоком уровне не всегда может помочь, например пакетное задание на компиляцию:

vector<string> fileNames = ...;
vector<string> errors;

for (size_t i = 0; i != fileNames.size(); i++)
{
    try
    {
        Compiler comp;
        comp.Compile(fileNames[i]);
    }
    catch (const CompileException &e)
    {
        errors[i] = e.getMessage();
    }
    errors[i] = ""; // Compilation ok.
}


Если, по каким-то причинам в процессе одной из компиляций вылезет левый ексепшен, то в случае catch(...) накроется только эта компиляция, а не все задание.

И, кстати говоря, от того что ты поставишь catch(...) на самом верхнем уровне, тебе будет точно также "ни жарко ни холодно", и ты точно также скроешь первопричину исключения.
http://www.lmdinnovative.com (LMD Design Pack)
Re[14]: try and catch, паранойя в использовании....
От: GlebZ Россия  
Дата: 10.06.05 08:05
Оценка:
Здравствуйте, IT, Вы писали:


IT> Т.е. из RFD выходят только исключения RFD. Ничего хорошего из этого не вышло. Практической пользы мизер, неудобств масса.

А конкретнее в чем выражаются неудобства, можно?

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