Re[7]: Какие у исключений проблемы?
От: AlexRK  
Дата: 07.11.14 15:25
Оценка: -1
Здравствуйте, jazzer, Вы писали:

J>Ну а теперь изобрази свой замечательный вариант на кодах возврата. С донесением всей необходимой информации на верхний уровень: если не открылся файл, то какой и почему, если сокет, то какой и почему.

J>Жду-пожду.

bool OpenSocket(string fileName, out int handle)
{
    handle = -1;

    var f = open_file(get_profile_dir(username) + fileName);

    if (f < 0)
    {
        Log.Write(GetLastErrorInfo());
        return false;
    }

    handle = open_socket(f);

    if (handle < 0)
    {
        Log.Write(GetLastErrorInfo());
        return false;
    }

    return true;
}

bool OpenAll()
{
    return
        OpenSocket("file1", out s1_) &&
        OpenSocket("file2", out s2_) &&
        OpenSocket("file3", out s3_) &&
        OpenSocket("file4", out s4_);
}


Изображать GOTO через все слои не вижу смысла, потому что и при наличии исключений на практике (а не в теории) бойлерплейт тоже присутствует в нехилом объеме.
Re[8]: Какие у исключений проблемы?
От: jazzer Россия Skype: enerjazzer
Дата: 07.11.14 17:12
Оценка: +1
Здравствуйте, alex_public, Вы писали:

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


_>>>Обсуждалось это всё уже давным давно) Причём и вместо с тобой. ))) Например здесь http://rsdn.ru/forum/cpp/4623107?tree=tree
Автор: alex_public
Дата: 18.02.12
(и там дальше по ветке основное будет) можно глянуть.

J>>Ну глянул. Моя позиция не изменилась. Ты там, вроде, мне не возражал.

_>Ну так консенсус (во всяком случае у меня с собеседникам) там сложился на формулировке "исключения хорошо подходят для обработки критических (т.е. после которых программа скорее всего прервёт исполнение) ошибок, а не всех видов ошибок вообще". Причём тут есть ещё хитрый нюанс — ошибки во многих API (типа того же открытия файла) могут быть как критическими, так и нет, в зависимости от контекста использования. Получается что по идее надо бы иметь по два вида API в таких библиотеках. Но Boost.Asio — это скорее исключение, чем правило...


_>В общем для большинства случаев (если не трогать таки вещи как скажем new) мне кажется, что лучше делать интерфейс без исключений. Ну или тогда уж оба варианта, как в Asio.


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

J>>а в реальном коде, даже если у нас в API только исключения, будет отдельная функция

J>>
J>>auto get_profile_or_default(string username)
J>>{
J>>  try {
J>>    return open_file(get_profile(username));
J>>  } catch(...) {
J>>    return open_file(get_default_profile());
J>>  }
J>>}
J>>

J>>и далее в коде все, как раньше:
J>>
J>>try {
J>>  f1  = get_profile_or_default(username);
J>>  s1_ = open_socket(f1); // читаем из конфига
J>>}
J>>catch(...) { std::throw_with_nested( EEnvSetup(username) ); }
J>>


_>Ну так разве это всё не ужас, по сравнению с тем моим кодом в 3 строчки? )


Ну так твой код нифига не делает. Конечно, он будет короче, чем код, который делает
У тебя же там даже кодов возврата нет.
Вместо кодов ошибок у тебя бинарная логика — сработало/не сработало, без какой-либо детализации.
Я хотел бы увидеть твой код, который делает все то же самое, что и мой, но на кодах возврата, тогда и сравним.

J>>Ну а теперь изобрази свой замечательный вариант на кодах возврата. С донесением всей необходимой информации на верхний уровень: если не открылся файл, то какой и почему, если сокет, то какой и почему.

J>>Жду-пожду.

_>Так а зачем выносить эту информацию на верхний уровень? ) Пользователю будешь это показывать? ))) Как раз это и обсуждалось в той старой темке...


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

Так что давай, изобрази. Покажи мощь и всеподходящесть кодов возврата.

J>>Да, продемонстрировал. И как, это было так ужасно, что ты демонстрацию стер с глаз долой? Целая одна строчка с std::throw_with_nested?


_>Не о том речь. Просто у исключений есть очевидные преимущества. Но они проявляются как раз для случая проброса через длинный стек вызова. Т.е. в идеальной модели с исключениями мы имеет вообще один единственный блок с try/catch на всю программу.


Давай не доводить до абсурда, хорошо?

_>Так вот, я как раз и утверждаю, что на практике такая модель может получиться только в очень специфических случаях. И ты только подтвердил это своим примером.


Очень интересно, где ты увидел в моем примере подтверждение своего тезиса про "очень специфические случаи"

_>Да, и я не говорю, что этот код сильно ужасен. Просто при такой схеме все преимущества исключений пропадают.


Где они тут пропадают? Можно пальцем показать?

_>А недостатки (например неявность ошибок) остаются.


Это еще что за зверь? И как насчет невозможности игнорирования исключений, по сравнению с повсеместным игнорированием кодов ошибок?

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

J>>Да, на верхнем уровне, скажем, в обработчике кнопок меню: прилетело исключение — рассказал пользователю, что облом (с показом по требованию всей собранной исключениями через throw_with_nested информации), и работаешь себе дальше.
J>>Потому что если можно продолжить нормальное исполнение программы после неудачного вызова функции на низком уровне, то это не исключительная ситуация по определению, правда?

_>Да, и тогда применять исключения не надо. Собственно о том и речь, что исключения хорошо подходят для решения только небольшой части общей задачи обработки ошибок.


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

_>В отличие от тех же кодов возврата, которые подходят везде.

Жаль, что так и не удалось послушать начальника транспортного цеха о том, как кодами возврата передается наверх информация об ошибке.
А то, может, я просто не знаю значения слова "везде"...
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[8]: Какие у исключений проблемы?
От: jazzer Россия Skype: enerjazzer
Дата: 07.11.14 17:17
Оценка: +3
Здравствуйте, AlexRK, Вы писали:

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


J>>Ну а теперь изобрази свой замечательный вариант на кодах возврата. С донесением всей необходимой информации на верхний уровень: если не открылся файл, то какой и почему, если сокет, то какой и почему.

J>>Жду-пожду.

ARK>
ARK>        Log.Write(GetLastErrorInfo());
ARK>


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

ARK>Изображать GOTO через все слои не вижу смысла, потому что и при наличии исключений на практике (а не в теории) бойлерплейт тоже присутствует в нехилом объеме.


А в моей практике никаких нехилых объемов не присутствует. ЧЯДНТ?
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[9]: Какие у исключений проблемы?
От: AlexRK  
Дата: 07.11.14 17:32
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну то есть вместо многообразия кодов возврата есть просто булевская логика, а информация об ошибке не передается наверх ни в каком виде. Желающие могут пройти в лог.


А зачем нужно многообразие кодов возврата? Когда сектор с диска прочитать не удается, там тоже тонна кодов возврата, только это обычно нафиг никому не нужно.
Текстовый лог просто как пример, информацию об ошибке можно писать в БД с нужной степенью детализации.

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


Вызов деструкторов инкапсулируется в ту же самую функцию OpenSocket (точно так же, как у вас работа с исключениями инкапсулирована в отдельной функции).

J>А в моей практике никаких нехилых объемов не присутствует. ЧЯДНТ?


А в моей присутствует. Кто из нас прав?
То, что с исключениями можно писать хороший код — никто не спорит. Но можно писать и не очень хороший. Вопрос в том, каково процентное соотношение того и другого.
И да, с кодами возврата тоже можно писать хороший код, даже операционные системы пишут.
Re: Какие у исключений проблемы?
От: Cyberax Марс  
Дата: 07.11.14 18:27
Оценка: 3 (2)
Здравствуйте, vsb, Вы писали:

vsb>В некоторых новых языках программирования (Go, Swift) уходят от механизма исключений и подавляющее число ошибок обрабатывается через явный возврат ошибки из вызываемой функции. В качестве аргумента часто показывают пальцем на язык С++. Наличие там исключений заставляет писать весь код так, как будто из любого вызова функции может вылететь исключение и размотать стек.

Я вчера разговаривал с Rust-овцами на Bay Area Rust Meetup

Что рассказали:
1) Исключения можно сделать хоть сейчас, это технически не проблема вообще. Тем более, что уже есть механизм unwinding'а.
2) Но их делать не будут из принципа.
3) Возможно, что в будущем принципы поменяют, если появится что-нибудь типа полноценной транзакционной памяти.

Аргумент такой, что исключения в надёжных системах возможно нормально использовать только при полной транзакционности кода ("strong exception guarantee"). Оказывается, что статически проверить обеспечение такой гарантии очень сложно (они пытались) и даже в академических языках её нет.

Потому они приняли решение сделать более мягкое решение, аналогичное "weak exception guarantee". В Rust при броске fail'а они убивают всю задачу, в которой код исполняется. Это обеспечивает вполне естественную границу для изоляции ошибки. Похожий подход и в Erlang'е применён, если что.

Ещё в качестве средства изоляции — при броске fail'а все разделяемые ресурсы (в Rust они есть в виде RWArc), которые в тот момент использовала задача, помечаются как "испорченные".

На вопрос про промежуточные слои они ответили, что у них тут вам не Java, и на практике промежуточных слоёв очень мало и почти всегда нужно делать семантическую трансляцию ошибок.
Sapienti sat!
Re[2]: Какие у исключений проблемы?
От: AlexRK  
Дата: 07.11.14 18:36
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Я вчера разговаривал с Rust-овцами на Bay Area Rust Meetup


Rust-овцы.
Смешно. Извините.

C>Что рассказали:

C>1) Исключения можно сделать хоть сейчас, это технически не проблема вообще. Тем более, что уже есть механизм unwinding'а.
C>2) Но их делать не будут из принципа.
C>3) Возможно, что в будущем принципы поменяют, если появится что-нибудь типа полноценной транзакционной памяти.



C>Аргумент такой, что исключения в надёжных системах возможно нормально использовать только при полной транзакционности кода ("strong exception guarantee"). Оказывается, что статически проверить обеспечение такой гарантии очень сложно (они пытались) и даже в академических языках её нет.


Блин, радует, что разработчики хоть одного ЯП это понимают.

C>Потому они приняли решение сделать более мягкое решение, аналогичное "weak exception guarantee". В Rust при броске fail'а они убивают всю задачу, в которой код исполняется. Это обеспечивает вполне естественную границу для изоляции ошибки. Похожий подход и в Erlang'е применён, если что.

C>Ещё в качестве средства изоляции — при броске fail'а все разделяемые ресурсы (в Rust они есть в виде RWArc), которые в тот момент использовала задача, помечаются как "испорченные".

По-хорошему, ИМХО, надо бы обязать все деструкторы ресурсов быть NoThrow и NoAllocate, и вызывать их при разрушении задачи в нужной последовательности.

C>На вопрос про промежуточные слои они ответили, что у них тут вам не Java, и на практике промежуточных слоёв очень мало и почти всегда нужно делать семантическую трансляцию ошибок.


И опять — молодцы.
Re[12]: Какие у исключений проблемы?
От: uncommon Ниоткуда  
Дата: 08.11.14 07:56
Оценка:
Здравствуйте, Vain, Вы писали:

V>ну значит расходы на вход в скоп:

V>
V>00E6100A  mov         eax,dword ptr fs:[00000000h]  
V>


Это x86. Под Windows. Фууу, брось каку! Там совсем другая реализация исключений.
Re[10]: Какие у исключений проблемы?
От: uncommon Ниоткуда  
Дата: 08.11.14 08:03
Оценка:
Здравствуйте, Vain, Вы писали:

V>Ну это не совсем так, при входе в try блок ведь чтото должно делаться, не так ли? Единственный вопрос, быстрее ли оно обычного условия в случае проверки кода возврата.


Во-первых, try/catch не надо вставлять куда попало. try/catch-ей должно быть очень и очень мало и только там, где понятно, что делать с исключениями. (А проверки ошибок возврата, как ты понимаешь, повсеместно.) Это, кстати, очень частая ошибка людей, которые не знают, как правильно использовать исключения. Во-вторых, нужно научится использовать RAII.
Re[2]: Какие у исключений проблемы?
От: uncommon Ниоткуда  
Дата: 08.11.14 08:08
Оценка: +1
Здравствуйте, alex_public, Вы писали:

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


_>Когда-то этот вопрос уже здесь обсуждался, причём именно для случая C++ (в контексте запрета исключений в гугловских руководствах).


В гугло-коде проблемы с исключениями по одной простой причине. Они не знают, что такое RAII, и выброс исключения порушит их код. В общем, они ССЗБ. Не их не жалко.
Re[10]: Какие у исключений проблемы?
От: uncommon Ниоткуда  
Дата: 08.11.14 08:19
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>А в моей присутствует. Кто из нас прав?


Покажи свой код. Дьявол, как обычно, в деталях.
Re[2]: Какие у исключений проблемы?
От: uncommon Ниоткуда  
Дата: 08.11.14 08:23
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Я вчера разговаривал с Rust-овцами на Bay Area Rust Meetup


C>Что рассказали:

C>1) Исключения можно сделать хоть сейчас, это технически не проблема вообще. Тем более, что уже есть механизм unwinding'а.
C>2) Но их делать не будут из принципа.

Ну, и будут каждую функцию заворачивать в try! или с монадами извращаться. Зато принцип!
Re[2]: Какие у исключений проблемы?
От: koodeer  
Дата: 08.11.14 08:33
Оценка:
Здравствуйте, Cyberax, Вы писали:

C> В Rust при броске fail'а они убивают всю задачу, в которой код исполняется.


C>Ещё в качестве средства изоляции — при броске fail'а все разделяемые ресурсы (в Rust они есть в виде RWArc), которые в тот момент использовала задача, помечаются как "испорченные".


Можно в двух словах для тех, кто не знаком с Rust'ом, что такое задача? Это процесс, поток или что-то ещё?
Меня интересует, если ресурс, например, файл, будет помечен как испорченный, то другой процесс, работающий с ним же, продолжит свою работу?
Re[3]: Какие у исключений проблемы?
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 08.11.14 08:39
Оценка: +1
Здравствуйте, koodeer, Вы писали:

C>>Ещё в качестве средства изоляции — при броске fail'а все разделяемые ресурсы (в Rust они есть в виде RWArc), которые в тот момент использовала задача, помечаются как "испорченные".


K>Можно в двух словах для тех, кто не знаком с Rust'ом, что такое задача? Это процесс, поток или что-то ещё?


Это навроде потока.
Re[11]: Какие у исключений проблемы?
От: dimgel Россия https://github.com/dimgel
Дата: 08.11.14 09:21
Оценка:
Здравствуйте, uncommon, Вы писали:

U>Во-первых, try/catch не надо вставлять куда попало. try/catch-ей должно быть очень и очень мало и только там, где понятно, что делать с исключениями. (А проверки ошибок возврата, как ты понимаешь, повсеместно.) Это, кстати, очень частая ошибка людей, которые не знают, как правильно использовать исключения.


Справедливости ради, в той же жаве try/finally приходится юзать на каждый чих, к примеру:

val stmt = conn.prepareStatement("...")
val result = try {
    ...
} finally {
    stmt.close()
}


UPD. Потому что деструкторов нету с авто-закрытием ресурсов при выходе из scope.
Отредактировано 08.11.2014 9:22 dimgel . Предыдущая версия .
Re: Какие у исключений проблемы?
От: Pavel Dvorkin Россия  
Дата: 08.11.14 09:45
Оценка: 10 (1)
Здравствуйте, vsb, Вы писали:

Добавлю свои три копейки.

1. Скорость обработки. Впрочем, от среды исполнения зависит. В нативных средах исключения реализуются через переход в кольцо 0 и обратным возвратом. Обработка исключений далеко не так проста, как кое-кому тут кажется.

Позволю себе процитировать достаточно большой кусок из Соломона-Руссиновича. Выделения мои — PD

/////////////////////////////////

All exceptions, except those simple enough to be resolved by the trap handler, are serviced by a kernel module called the exception dispatcher. The exception dispatcher's job is to find an exception handler that can "dispose of" the exception. Examples of architecture-independent exceptions that the kernel defines include memory access violations, integer divide-by-zero, integer overflow, floating-point exceptions, and debugger breakpoints. For a complete list of architecture-independent exceptions, consult the Win32 API reference documentation.

...

A few exceptions are allowed to filter back, untouched, to user mode. For example, a memory access violation or an arithmetic overflow generates an exception that the operating system doesn't handle. An environment subsystem can establish frame-based exception handlers to deal with these exceptions. The term frame-based refers to an exception handler's association with a particular procedure activation. When a procedure is invoked, a stack frame representing that activation of the procedure is pushed onto the stack. A stack frame can have one or more exception handlers associated with it, each of which protects a particular block of code in the source program. When an exception occurs, the kernel searches for an exception handler associated with the current stack frame. If none exists, the kernel searches for an exception handler associated with the previous stack frame, and so on, until it finds a frame-based exception handler. If no exception handler is found, the kernel calls its own default exception handlers.

When an exception occurs, whether it is explicitly raised by software or implicitly raised by hardware, a chain of events begins in the kernel. The CPU hardware transfers control to the kernel trap handler, which creates a trap frame (as it does when an interrupt occurs). The trap frame allows the system to resume where it left off if the exception is resolved. The trap handler also creates an exception record that contains the reason for the exception and other pertinent information.

...

If the exception occurred in user mode, the exception dispatcher does something more elaborate. As you'll see in Chapter 6, the Win32 subsystem has a debugger port and an exception port to receive notification of user-mode exceptions in Win32 processes. The kernel uses these in its default exception handling, as illustrated in Figure 3-6.


Debugger breakpoints are common sources of exceptions. Therefore, the first action the exception dispatcher takes is to see whether the process that incurred the exception has an associated debugger process. If so, it sends the first-chance debug message (via an LPC port) to the debugger port associated with the process that incurred the exception. (The message is sent to the session manager process, which then dispatches it to the appropriate debugger process.)

If the process has no debugger process attached, or if the debugger doesn't handle the exception, the exception dispatcher switches into user mode and calls a routine to find a frame-based exception handler. If none is found, or if none handles the exception, the exception dispatcher switches back into kernel mode and calls the debugger again to allow the user to do more debugging. (This is called the second-chance notification.)

All Win32 threads have an exception handler declared at the top of the stack that processes unhandled exceptions. This exception handler is declared in the internal Win32 start-of-process or start-of-thread function. The start-of-process function runs when the first thread in a process begins execution. It calls the main entry point in the image. The start-of-thread function runs when a user creates additional threads. It calls the user-supplied thread start routine specified in the CreateThread call.

///////////////////////////////

2. Невозможность не обрабатывать исключения. Коды возврата можно учитывать или игнорировать, то есть их можно классифицировать по обычной схеме как ошибки, предупреждения или информационные сообщения; второе и третье при желании можно игнорировать.
With best regards
Pavel Dvorkin
Re[11]: Какие у исключений проблемы?
От: AlexRK  
Дата: 08.11.14 10:28
Оценка:
Здравствуйте, uncommon, Вы писали:

ARK>>А в моей присутствует. Кто из нас прав?

U>Покажи свой код. Дьявол, как обычно, в деталях.

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

Хотя, честно говоря, не вижу смысла в этом. То, что многие (а из мною лично виденных — все без исключения) крупные системы содержат кучу мусора с try/catch/finally — это факт. То, что это можно переписать лучшим образом — тоже факт. То, что это не переписывается — тоже факт. О чем спор?
Re[3]: Какие у исключений проблемы?
От: AlexRK  
Дата: 08.11.14 10:30
Оценка:
Здравствуйте, uncommon, Вы писали:

C>>Что рассказали:

C>>1) Исключения можно сделать хоть сейчас, это технически не проблема вообще. Тем более, что уже есть механизм unwinding'а.
C>>2) Но их делать не будут из принципа.

U>Ну, и будут каждую функцию заворачивать в try! или с монадами извращаться. Зато принцип!


Маразм с каждой функцией нужен только в языках, не имеющих контроля за исключениями, в которых исключение может вылететь где угодно (то есть во всех менйстримовых ЯП).
Re[11]: Какие у исключений проблемы?
От: AlexRK  
Дата: 08.11.14 10:32
Оценка: :)
Здравствуйте, uncommon, Вы писали:

U>Во-вторых, нужно научится использовать RAII.


Ога. Вот тут про это хорошо сказано: http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758 ("Solution 3: The Real Approach").
Re[2]: Какие у исключений проблемы?
От: smeeld  
Дата: 08.11.14 10:34
Оценка: :)
Здравствуйте, Pavel Dvorkin, Вы писали:


PD>Добавлю свои три копейки.


Вы немного не в ту степь завернули. Тут говорилось не про hardware exceptions.
Тут говорилось про нужный и качественный механизм обработки катастрофических для программ обстоятельств,
обеспечивающих невозможность нормального её исполнения, появивишийся однажды в Lisp, и впоследствии
раздербаненный стадом обезьян, и изуродованный, и растасканный по всем закоулкам, по самое не балуйся.
Обезьяны наигрались, и теперь, на некоторых деревьях, начинают терять интерес к игрушке, типа она такая
кошмарная как в С++. Не надо было за эталон брать то, что в С++, академики праздные, накорябали своими белыми ручонками.
Re[11]: Какие у исключений проблемы?
От: Vain Россия google.ru
Дата: 08.11.14 11:22
Оценка:
Здравствуйте, uncommon, Вы писали:

V>>Ну это не совсем так, при входе в try блок ведь чтото должно делаться, не так ли? Единственный вопрос, быстрее ли оно обычного условия в случае проверки кода возврата.

U>Во-первых, try/catch не надо вставлять куда попало.
Не выходит оно так, приходится вставлять везде где надо пробрасывать исключение, т.е. почти везде.

U>try/catch-ей должно быть очень и очень мало и только там, где понятно, что делать с исключениями. (А проверки ошибок возврата, как ты понимаешь, повсеместно.) Это, кстати, очень частая ошибка людей, которые не знают, как правильно использовать исключения. Во-вторых, нужно научится использовать RAII.

Наивняк.. Это ты ещё корбу и другие библы не использовал.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.