Здравствуйте, Sinclair, Вы писали:
S>Я плохо знаком с функциональными языками, но мне кажется очевидным вариант, при котором и бойлерплейта меньше, и с валидностью всё хорошо. S>Что мы тут делаем? Конструируем структуру меню. S>Пусть каждая функция возвращает либо узел, либо ошибку. И алгебра достаточно тривиальная — объединение (узел & ошибка) даёт ошибку, объединение (узел & узел) даёт узел.
Оно там именно так и устроенно — монада Exceptional:
Exception l >>= _ = Exception l
Success r >>= k = k r
По сути — те же самые исключения. Особенность в том, что функции для того чтобы "выбросить" исключение нужно менять сигнатуру.
S>Если мы хотим проигнорировать ошибку, то нам придётся вручную заматчить ошибку и превратить её в void.
Здравствуйте, MTD, Вы писали:
MTD>На практике это неудобно, например, я не хочу сдвигать итератор, просто хочу проверить, что есть куда сдвигать.
А он и не сдвигается, если сдвигать некуда.
MTD>А MoveNext невалидному итератору? Ты уверен, что стоит так замаскировать баг?
Нет никакого бага. Итератор находится в своём финишном состоянии, куда он перешел от последнего вызова MoveNext. Последующие вызовы MoveNext ничего не делают.
Здравствуйте, Sinclair, Вы писали:
S>Фантазии оставьте. Даже если вы сумеете найти хэндл GC-потока и прибить, то среда просто перезапустит его.
"Кто-то" должен перезапустить поток, а этот "кто-то" остановлен.
А найти хендл потока — проще простого, просто перебрать потоки и найти те, которые не текущий (и не были созданы в ходе работы программы).
Да и вообще — это всё спекуляции. В С++ нет способа убить поток, это делается через системный АПИ, через который можно поломать вообще что угодно при должном усердии.
If the target thread owns a critical section, the critical section will not be released.
If the target thread is allocating memory from the heap, the heap lock will not be released.
If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.
Таким образом, через TerminateThread можно поломать что угодно.
Но можно попробовать от этого защититься:
A thread cannot protect itself against TerminateThread, other than by controlling access to its handles. The thread handle returned by the CreateThread and CreateProcess functions has THREAD_TERMINATE access, so any caller holding one of these handles can terminate your thread.
Итого, для создания потока можно использовать некий хелпер, который вызовет CreateThread с неким security descriptor, в котором запретит THREAD_TERMINATE access, ну или через DuplicateHandle создаст другой дескриптор с ограниченными правами, а старый прибьет..
Здравствуйте, vdimas, Вы писали:
V>Таким образом, через TerminateThread можно поломать что угодно.
А через Thread.Abort — нет. В С++, как верно замечено, стыдливо игнорируют наличие потоков, поэтому нет выбора кроме адски небезопасных механизмов ОС.
В управляемой среде есть штатный способ прибить поток (хотя его использование и является порочной практикой)
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
V>>Таким образом, через TerminateThread можно поломать что угодно. S>А через Thread.Abort — нет.
Он может выкинуть исключение откуда угодно? Или только из обозначенных мест?
S>В С++, как верно замечено, стыдливо игнорируют наличие потоков, поэтому нет выбора кроме адски небезопасных механизмов ОС.
Я же уже выше сказал, что используются cancellation points раскручивающие стэк
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Он может выкинуть исключение откуда угодно? Или только из обозначенных мест?
Откуда угодно. Но при этом поток не умирает сразу — в нём отрабатывают секции finally и хэндлеры исключений.
EP>Я же уже выше сказал, что используются cancellation points раскручивающие стэк
Я не знаком с этим термином. Можно посмотреть на его описание?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
EP>>Он может выкинуть исключение откуда угодно? Или только из обозначенных мест? S>Откуда угодно.
Очень хрупкая концепция — получается что no-throw кода нет в принципе, что в некоторых случаях сильно затрудняет реализацию транзакционных операций.
Надеюсь оно хоть не выкидывает новое исключение при повтором Thread.Abort в случае когда первое поймано или при выполнении finally/dispose?
S>Но при этом поток не умирает сразу в нём отрабатывают секции finally и хэндлеры исключений.
Это само собой.
EP>>Я же уже выше сказал, что используются cancellation points раскручивающие стэк S>Я не знаком с этим термином. Можно посмотреть на его описание?
Точки из которых может вылететь thread cancelation exception задаются явным образом.
Здравствуйте, Sinclair, Вы писали:
V>>Таким образом, через TerminateThread можно поломать что угодно. S>А через Thread.Abort — нет. В С++, как верно замечено, стыдливо игнорируют наличие потоков,
Начиная со стандарта C++11 — все ОК.
S>поэтому нет выбора кроме адски небезопасных механизмов ОС.
Почему "нет выбора"?
Для MSVC есть CRT _beginthread/_beginthreadex, в мире Linux есть библиотека pthread. Ср-ва этих библиотек являются безопасными.
S>В управляемой среде есть штатный способ прибить поток (хотя его использование и является порочной практикой)
А кто мешает вызвать TerminateThread через p-Invoke?
Ну я напомню, что этот "штатный способ" на самом деле — лишь библиотечная хрень, которая реализована даже в boost.
Например, в Linux потоку посылается signal, и при заходе в любое ядерное АПИ вызывается этот сигнал в контексте потока.
В Windows есть аналогичный способ прервать состояние ожидания на другом потоке — через APC.
А дотнетная среда может безопасно прибивать потоки только в "ловушках" для GC, коих нет, например, в коде 10 GOTO 10. Этот случай можно прибить только через TerminateThread и я предполагаю, что после попыток "безопасного" прибивания потока и безуспешного ожидания в течении некоторого времени, дотнетная среда обязательно должна пытаться сделать "опасное" прибивание той самой ситуации 10 GOTO 10. И еще я думаю, что при этом не гарантируется безопасность — вдруг именно в этот самый момент будет вызвано системное АПИ? ))
Вот и автор ZeroMQ решил, что эта сложность только мешает, и разработчики Rust с ним согласны. У нас нет исключений, а потенциальные ошибки являются частью возвращаемых (алгебраических) типов: http://250bpm.com/blog:4
Здравствуйте, s22, Вы писали:
s22>У нас нет исключений, а потенциальные ошибки являются частью возвращаемых (алгебраических) типов:
Тоже вариант (в C++ есть подобный proposal — expected), но в случае если функция не возвращает значение не особо подходит.
s22>http://250bpm.com/blog:4
А вот это
However, consider what happens when there are two different errors thrown in the same function:
class exception1 {};
class exception2 {};
try {
...
if (condition1)
throw my_exception1 ();
...
if (condition2)
throw my_exception2 ();
...
}
catch (my_exception1 &e) {
handle_exception1 ();
}
catch (my_exception2 &e) {
handle_exception2 ();
}
Compare that to its C equivalent:
...
if (condition1)
handle_exception1 ();
...
if (condition2)
handle_exception2 ();
...
It's far more readable and — as a bonus — compiler is likely to produce more efficient code.
Здравствуйте, Mamut, Вы писали:
M>Ничем не отличается от exception'ов Пример кода из самого Раста я приводил. Там пятиуровневая конструкция, из недр которой наверх возвращается некий ErrorReport. Чем он отличается от самодельного ThrownExceptionsCollection? Да ничем
If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
Т.е. всё уже есть. В том или ином виде аналогичный WhenAll есть практически во всех фреймворках, оперирующих future/task/deferred.
Здравствуйте, vdimas, Вы писали:
V>Тебя коллега вводит в заблуждение. Для примера, дотнетный библиотечный Task.WhenAll в дотнете вернет первую же ошибку, если она возникла у любой из ожидаемых асинхронных задач: V>https://msdn.microsoft.com/en-us/library/hh160384(v=vs.110).aspx V>
V>If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
V>If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
V>If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
Цитата опровергает ваше утверждение. Но речь не об этом, а о том, что каждое такое решение надо велосипедить заново, никаких готовых механизмов для этого нет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>И далее всё в таком духе
А что еще, например, "в таком духе"?
Это вот что — неправда?
Consider the case when the exception is not handled in the function that raises it. In such case the handling of the error can happen anywhere, depending on where the function is called from.
While the possibility to handle the exceptions differently in different contexts may seem appealing at the first sight, it quickly turns into a nightmare.
Просто у него приложение такое, что ошибки надо обрабатывать близко к месту возникновения, а не просто кидать куда-то в самый верх и писать там в лог.
Об этом он тоже, кстати, пишет.
Здравствуйте, AlexRK, Вы писали:
EP>>И далее всё в таком духе ARK>А что еще, например, "в таком духе"?
Например вот это:
With C, the raising of the error and handling it are tightly couped and reside at the same place in the source code. This makes it easy to understand what happens if error happens:
int rc = fx ();
if (rc != 0)
handle_error ();
With C++ you just throw the error. What happens then is not at all obvious:
int rc = fx ();
if (rc != 0)
throw std::exception ();
The problem with that is that you have no idea of who and where is going to handle the exception.
ARK>Это вот что — неправда? ARK>
Consider the case when the exception is not handled in the function that raises it. In such case the handling of the error can happen anywhere, depending on where the function is called from.
ARK>While the possibility to handle the exceptions differently in different contexts may seem appealing at the first sight, it quickly turns into a nightmare.
Это демагогия и передёргивание. При return error_code; — получаем ровно тоже самое "handling of the error can happen anywhere", только с на порядок большим колличеством boilerplatе'а типа ручных цепочек goto cleaunpN; на всём пути от места исключения до обработчика
Если же обработка ошибки происходит сразу же — то никаких исключений кидать не надо, также как не надо фигурно вырезать goto cleaunpN;.
Здравствуйте, VladD2, Вы писали:
s22>>в инете много про это.... s22>>часто компиляторы не инлайнят код, если во вложенной функции есть вызов исключения. VD>Это проблемы не исключений, а С++ и его компиляторов. И не смотря на эти проблемы С++ считается одним из самых "быстрых" языков (т.е. его компиляторы порождают самый быстрый код).
Чтобы нормально ловить и обрабатывать исключения приходится их перебрасывать (т.н. трюк с rethrow) либо в макросе (чтобы гарантировать что макрос вставили в конце цепочки catch'ей) либо в функции (для эстетов, но также с переброской). Это приводит к "испарению" преимущества по производительности исключений по сравнению с кодами возвратов.
А теперь расскажи нам какой компилятор сможет перебрасывать исключения без потери производительности?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
V>Чтобы нормально ловить и обрабатывать исключения приходится их перебрасывать (т.н. трюк с rethrow) либо в макросе (чтобы гарантировать что макрос вставили в конце цепочки catch'ей) либо в функции (для эстетов, но также с переброской). Это приводит к "испарению" преимущества по производительности исключений по сравнению с кодами возвратов. V>А теперь расскажи нам какой компилятор сможет перебрасывать исключения без потери производительности?
Уж сколько раз твердили миру... Исключения — они вылетают в _исключительных_ ситуациях, которые случаются _редко_. И в этом случае мы имеем высокую производительность пока их не происходит. Если же их использовать как goto, то конечно, про производительность можно забыть.
Ну вот есть функция, которая возвращает да/нет, и в зависимости от результата идет ветвление. Глупо было бы это делать на исключениях. А вот если по какой-то причине (нехватка ресурсов, поврежденные данные, и т.п.) она не может вернуть корректный результат, то можно и бросить.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Ops, Вы писали:
Ops>Здравствуйте, Vain, Вы писали:
V>>Чтобы нормально ловить и обрабатывать исключения приходится их перебрасывать (т.н. трюк с rethrow) либо в макросе (чтобы гарантировать что макрос вставили в конце цепочки catch'ей) либо в функции (для эстетов, но также с переброской). Это приводит к "испарению" преимущества по производительности исключений по сравнению с кодами возвратов. V>>А теперь расскажи нам какой компилятор сможет перебрасывать исключения без потери производительности? Ops>Уж сколько раз твердили миру... Исключения — они вылетают в _исключительных_ ситуациях, которые случаются _редко_. И в этом случае мы имеем высокую производительность пока их не происходит. Если же их использовать как goto, то конечно, про производительность можно забыть. Ops>Ну вот есть функция, которая возвращает да/нет, и в зависимости от результата идет ветвление. Глупо было бы это делать на исключениях. А вот если по какой-то причине (нехватка ресурсов, поврежденные данные, и т.п.) она не может вернуть корректный результат, то можно и бросить.
goto выполняется 0 тактов
так же как и условный переход, если он правильно предсказан.
Здравствуйте, Ops, Вы писали:
Ops>Уж сколько раз твердили миру... Исключения — они вылетают в _исключительных_ ситуациях, которые случаются _редко_. И в этом случае мы имеем высокую производительность пока их не происходит. Если же их использовать как goto, то конечно, про производительность можно забыть.
Код с исключениями медленнее кода без исключений.... из за проблем с инлайнингом, это существенно на уровне оптимизации 2 или 3.