LINUX.ORG.RU

Деструкторы не нужны (?)

 


0

5

Тут в соседней теме один анон сказанул следующее:

Дело не в том. Сишка, она про написание руками + можно навесит абстракций, аки глиб/гобджект. Кресты же — изначально нагромождение абстракций со строками, срущими в кучу, функциональными объектами, методами, вызывающимися неявно (например, деструкторы, на которые жаловался кармак) и проч

Собственно, хотелось бы поговорить о выделенном.

Антон прикрылся ссылкой, по которой про деструкторы я так ничего и не нашёл. Более того, в твиттере Кармака всё выглядит с точностью до наоборот — https://twitter.com/id_aa_carmack/status/172340532419375104

RAII constructor / destructor pairing and templates for type safe collections are pretty high on my list of C++ benefits over C

Кто прав? Кто виноват? И нужны ли в итоге C++ деструкторы?

Ответ на: комментарий от anonymous

В итоге, при вызове любой функции источник ошибки всегда ровно один - возвращаемое значение

Это вы от большого ума путаете источник ошибки и способ информирования об ошибке? Или вы так троллить пытаетесь?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Это вы от большого ума путаете источник ошибки и способ информирования об ошибке? Или вы так троллить пытаетесь?

Нет, дорогой эксперт, я не путаю :-) Под «источником ошибки» я понимаю в данном случае место в коде, в котором последний раз произошла передача объекта с информацией об ошибке :-) Понятно же, что среднестатистический C++-писатель не будет транслировать исключения из зависимостей в исключения своего модуля, ведь зачем утруждать себя такой неблагородной работой, когда любую функцию можно сделать нейтральной по отношению к исключениям используемых зависимостей :-) А вот с кодами возврата, понятное дело, надо работать - надо обрабатывать ошибку каждого вызова и если не знаешь что с ней делать, передавать вызывающему коду :-) В итоге получается, что каждая вызываемая функция является «источником ошибки», если возвращает объект ошибки :-)

anonymous
()
Ответ на: комментарий от anonymous

Под «источником ошибки» я понимаю в данном случае место в коде, в котором последний раз произошла передача объекта с информацией об ошибке

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

Тему-то с неоходимостью знать про вообще все возможные ошибки вы ловко закрыли. Типа никто не заметил.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

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

Покажи хоть одно высказывание, которое выглядит «бредово» :-) Конечно, можно понять, что «не бредово» выглядят лишь решения комитета C++ и тех, кто ничего кроме этих решений не признаёт и даже не хочет признавать факты явных просчётов в дизайне :-)

Тему-то с неоходимостью знать про вообще все возможные ошибки вы ловко закрыли. Типа никто не заметил.

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

anonymous
()
Ответ на: комментарий от anonymous

Покажи хоть одно высказывание, которое выглядит «бредово»

Да как два пальца:

В итоге, при вызове любой функции источник ошибки всегда ровно один - возвращаемое значение, в отличии от исключений, которые могут прилетать из 100500 мест :-)

Количество источников ошибок никак не зависит от того, возвращает ли функция код ошибки или бросает исключение.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да как два пальца:
Количество источников ошибок никак не зависит от того, возвращает ли функция код ошибки или бросает исключение.

Ниже я пояснил, что я понимаю под «источником ошибки» :-) Но ты решил поумничать :-)

В принципе, всё ясно, своё занудство ты решил превратить в чьи-то бредни :-) С больной головы на здоровую это называется :-) Слабенький такой приёмчик, который обычно применяется когда возразить нечем, но и принимать правоту или аргументы собеседника не желательно, и необходимо последнее слово оставить за собой :-) Часто применяется на всяких коллективных собраниях жильцов многоквартирных домов :-) Лол :-)

anonymous
()
Ответ на: комментарий от anonymous

Ниже я пояснил, что я понимаю под «источником ошибки»

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

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

Так что тут не о чем умничать. Остается пожалеть, что некий улыбчивый дурень не понимает, что сохранение инвариантов нужно вне зависимости от способа информирования об ошибках. И что RAII прекрасно работает и в том, и в другом случаях. А не понимая этого приходит просраться в каждую тему по C++.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Вы не в состоянии называть вещи своими именами.

Ты не в состоянии понять, что писать в доке к библиотеки Mylib, что «ф-я Mylib::f() может генерировать ошибку Mylib::E и ещё любую ошибку из зависимостей Mylib, ищите сами, разбирайтесь сами, я тут не причём, это всё исключения цепепе» гораздо хуже, чем написать в доке точный перечень возвращаемых Mylib::f() ошибок :-) Конечно, если в зависимостях лишь стандартная библиотека, то это ещё куда ни шло :-) Хотя, я не удивлюсь, что скоро в стандарте появится и сборщик мусора, и СУБД, и HTTP сервер, и даже браузер с редактором, и стандарт будет тысяч шесть страниц :-)

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

Гадать можно долго, но мимо :-)

Так что тут не о чем умничать. Остается пожалеть, что некий улыбчивый дурень не понимает, что сохранение инвариантов нужно вне зависимости от способа информирования об ошибках. И что RAII прекрасно работает и в том, и в другом случаях. А не понимая этого приходит просраться в каждую тему по C++.

Причём тут инварианты? :-) Причём тут RAII? :-) Америку открывать не обязательно :-) Дорогой эксперт, я говорю об использовании библиотек для хвалёного повторного использования, из каждой из которых может выпрыгнуть неожиданное исключение, которое мне, возможно, надо будет транслировать в свои коды/исключения :-) А чтобы это сделать, мне надо знать, какое именно исключение прилетит :-) Не все же должны ограничиваться унылым RAII на деструкторах и записями в лог :-) Бывают и другие задачи :-) Лол :-)

anonymous
()
Ответ на: комментарий от anonymous

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

Т.е. вы не можете, а виноват C++ и использование исключений в нем? Прелестно.

Поскажите, пожалуйста, какую-нибудь современную C++ библиотеку, которая выкидывает наружу исключения, не отнаследованные от std::exception.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Т.е. вы не можете, а виноват C++ и использование исключений в нем? Прелестно.

Чего не могу, лазить по чужому коду, чтобы выяснить какие исключения генерятся? :-) Могу, но не хочу :-) Мне просто вспоминаются невольно фантазии о том, что «разработчику достаточно будет ознакомиться с интерфейсом, не вникая в реализацию» :-) Ну да, как же :-)

Поскажите, пожалуйста, какую-нибудь современную C++ библиотеку, которая выкидывает наружу исключения, не отнаследованные от std::exception.

Страуструп, например, писал в своей толстенной книге, что не считает это необходимым :-) Хотя в бусте, кажется, рекомендовали всегда наследоваться, причём виртуально, от std::exception :-) Хочешь сказать, что все прям виртуально наследуются от std::exception? :-) Да и что толку от std::exception? :-) Всё, что он может предложить, это унылый what() :-) Что мне с ним делать, кроме как в лог? :-)

anonymous
()
Ответ на: комментарий от anonymous

Чего не могу, лазить по чужому коду, чтобы выяснить какие исключения генерятся?

В нормальных библиотеках есть базовый класс исключений, используемый этой библиотекой. Об этом сказано в документации и показано в примерах. Так что у обычных программистов это не вызвает совсем никаких проблем.

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

Хочешь сказать, что все прям виртуально наследуются от std::exception

Еще раз повторю вопрос: покажите современную библиотеку, исключения в которой не отнаследованы от std::exception. Хоть здесь-то вы как-то сможете подтвердить свои слова? Или будете продолжать лужи газифицировать.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

В нормальных библиотеках есть базовый класс исключений, используемый этой библиотекой. Об этом сказано в документации и показано в примерах. Так что у обычных программистов это не вызвает совсем никаких проблем.

Нормальные библиотеки могут зависеть от других библиотек, которые могут зависеть от других библиотек и т.д. :-) Теперь покажи мне библиотеки, которые транслирует все исключения зависимостей в наследников указанного тобой «базового класса исключений» :-)

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

А твоё остроумие почему-то никак не позволяет тебе дойти до того, о чём тебе говорят :-)

Еще раз повторю вопрос: покажите современную библиотеку, исключения в которой не отнаследованы от std::exception. Хоть здесь-то вы как-то сможете подтвердить свои слова? Или будете продолжать лужи газифицировать.

Да это не суть важно :-) См. первый абзац :-)

anonymous
()
Ответ на: комментарий от anonymous

Теперь покажи мне библиотеки, которые транслирует все исключения зависимостей в наследников указанного тобой «базового класса исключений»

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

anonymous
()
Ответ на: комментарий от anonymous

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

Поэтому нормальная библиотека будет наполнена try/catch точно так же, как если бы это были if для проверки кодов возвратов :-) За это и спич, собственно :-)

anonymous
()
Ответ на: комментарий от anonymous

Чего не могу, лазить по чужому коду, чтобы выяснить какие исключения генерятся? :-) Могу, но не хочу :-) Мне просто вспоминаются невольно фантазии о том, что «разработчику достаточно будет ознакомиться с интерфейсом, не вникая в реализацию» :-) Ну да, как же :-)

выбрасываемые фактически исключения есть частью API. так что если документация вида «название функции, разбитое на слова», то без читки кода интерфейс непонятен. throw() использовать не вариант хотя бы с точки зрения производительности.

dzidzitop ★★
()
Ответ на: комментарий от dzidzitop

https://fossies.org/diffs/qt-everywhere-opensource-src/5.5.1_vs_5.6.0/qtbase/...

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

This is the complete list of members for QException, including inherited members.

clone() const : QException * raise() const

dzidzitop ★★
()
Ответ на: комментарий от dzidzitop

Во-первых, речь шла о современных библиотеках. Qt же появилась тогда, когда стандарта C++98 (следовательно и std::exception) еще не было.

Во-вторых, в исходниках все не так однозначно:

class Q_CORE_EXPORT QException : public std::exception
{
public:
    ~QException()
#ifdef Q_COMPILER_NOEXCEPT
    noexcept
#else
    throw()
#endif
    ;
    virtual void raise() const;
    virtual QException *clone() const;
};

В третьих, сам Qt еще с исключениями не очень дружит и exception safety не гарантирует: http://doc.qt.io/qt-5/exceptionsafety.html Так что приводить Qt в качестве примера библиотеки с исключениями довольно странно. Впрочем, C++ хейтеры — они такие... Подумать лишний раз лень.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

C++ хейтеры

имея три некоммерческих проекта на C++, скажу, что это синдром «кто не согласен со мной, тот лох, скотина и подлец».

по теме (не для eao197): сколько бы библиотек на плюсах использовали или не использовали исключительно std::exception, а всё равно если нужен гарантированный API, то нужно всё, что смотрит наружу оборачивать в catch (...) с потерей информации о том, что происходит. а оборачивать в catch (std::exception &) { throw xxx; } catch (...) { throw yyy; } достаточно громоздко. не видел библиотечного кода, который так делает.

dzidzitop ★★
()
Ответ на: комментарий от dzidzitop

имея три некоммерческих проекта на C++

Просто удивительно, как тут обошлось без «И встать, когда разговаривает подпоручик!» (с)

Хотите подтвердить свои слова размером своего опыта — просто озвучивайте длину в сантиметрах.

По теме (в том числе и для dzidzitop):

- если речь идет про C API, то все исключения придется ловить вне зависимости от языка программирования (будь то C++, D или OCaml с исключениями, или Rust или Go с паниками);

- если речь идет про C++ API, то что понимается под гарантированным API вообще не понятно. Если библиотеку X собрали компилятором Y версии N, то далеко не факт, что ее получится слинковать с приложением, собранным компилятором же Y, но версии (N+k).

Кроме того, поскольку C++ уверенно движется в сторону обобщенного кода и header-only библиотек на шаблонах, тезисы о необходимости перехвата «чужих» исключений и трансформации их в «свои» исключения становятся довольно мутными.

Например, допустим Вася Пупкин написал шаблонную библиотеку конкурентных очередей, в которой есть класс вида:

template<typename T, typename Mutex, typename Condition>
class multicore_friendly_queue {...};
Подсовываешь ей типы Mutex-а и Condition-а, которые являются аналогами std::mutex и std::conditon, и работаешь.

А какой-нибудь Ваня Иванов написал библиотеку примитивов синхронизации, которые могут заменять std::mutex и std::condition. И примитивы эти в каких-то случаях бросают какие-то собственные исключения.

А какой-нибудь Вова Сидоров, захотел все это объединить в своем проекте. И что, по версии dzidzitop и улыбчивых анонимов, в методах multicore_friendly_queue должно быть что-то вида:

template<typename T, typename Mutex, typename Condition>
consume_result<T> multicore_friendly_queue::consume() {
  try {
    std::unique_lock< Mutex > lock{ lock_ };
    ...
  }
  catch(...) {
    throw some_concurrent_queue_lib_exception(...);
  }
}
?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

если речь идет про C++ API, то что понимается под гарантированным API вообще не понятно. Если библиотеку X собрали компилятором Y версии N, то далеко не факт, что ее получится слинковать с приложением, собранным компилятором же Y, но версии (N+k).

Так это же C++ как он есть :-) Лол :-)

Кроме того, поскольку C++ уверенно движется в сторону обобщенного кода и header-only библиотек на шаблонах, тезисы о необходимости перехвата «чужих» исключений и трансформации их в «свои» исключения становятся довольно мутными.

Ну если C++ уверенно движется в сторону тормозной компиляции, где на выходе получается бинарь, который не предназначен для вызова всей скомпиленной шаблонной мути из других языков, тогда ок, можно не перехватывать :-) Но ведь Страуструп то в своей «Дизайн и эволюция C++» говорил про то, что C++ - это всего лишь один из языков в системе, в отличии от подхода Лиспа, который задумывался как всеобъемлющая среда :-) Поэтому мне не понятно это «уверенное движение» :-)

И что, по версии dzidzitop и улыбчивых анонимов, в методах multicore_friendly_queue должно быть что-то вида:

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

anonymous
()
Ответ на: комментарий от anonymous

Ну если C++ уверенно движется в сторону тормозной компиляции

Он всегда там был

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

Это с самого начало было так.

Поэтому мне не понятно это «уверенное движение»

Этом потому, что вы дебил, сударь.

eao197 ★★★★★
()

Я тут подумал, а зачем нам знать тип исключения? Мне приходит в голову только один вариант: если вызываемая функция нишмагла что-то сделать, в зависимости от причины попробовать устранить её и попытаться снова. Но, как мне кажется, с этим отлично справляется паттерн «Стратегия».

Если, например, имеем:

while (нишмагли) {
  try {
    записать_большой_файл();
  }
  catch(места нет) {
    почистить tmp;
    места больше не стало -> break;
  }
}

Получим:

  bool нишмагла = записать_большой_файл(&решатель_проблем);
  
  ***
  
  bool решатель_проблем::нет_места()
  {
    почистить tmp;
    return стало ли больше места;
  }

В таком виде ф-ции достаточно сигналить вызывающей стороне только двумя состояниями: смогла, не смогла.

uuwaan ★★
()
Ответ на: комментарий от uuwaan

Я тут подумал, а зачем нам знать тип исключения?

Ну вот и первый правильный вопрос за долгое время!

eao197 ★★★★★
()
Ответ на: комментарий от uuwaan

Я тут подумал, а зачем нам знать тип исключения?

Потому что объект исключения может содержать разную произвольную информацию в зависимости от типа :-) Для этого и принято было изначально делать иерархии классов исключений, как и принято было делать типы исключений полиморфными :-)

А так то и std::exception с его what() не нужны, ведь можно просто передавать код как throw -1, throw 127 и т.д. :-)

В таком виде ф-ции достаточно сигналить вызывающей стороне только двумя состояниями: смогла, не смогла.

Частный случай как он есть :-)

anonymous
()
Ответ на: комментарий от anonymous

Потому что объект исключения может содержать разную произвольную информацию в зависимости от типа

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

Частный случай как он есть :-)

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

uuwaan ★★
()
Ответ на: комментарий от uuwaan

Для чего эта информация нужна?

Для восстановления :-)

Если для попытки устранить причину ошибки, в моём примере нужно просто отдать её коллбэку, пытающемуся устранить проблему.

Кого её? :-) В твоём примере самодостаточный bool, который ты и передаёшь, потому что тебе достаточно :-)

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

А если тебе надо структурировать эту информацию в несколько полей? :-) Парсер писать изволишь, чтобы в catch им парсить? :-) Лол :-)

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

Что, кому-то захотелось возни с коллбэками? :-) Если передавать в функции «решатели проблем», то ты быстро утомишься, потому что проблемные функции могут зависеть от других проблемных функций и т.д. :-) Зачем это надо, когда есть исключения и/или возвращаемые значения? :-)

Теперь пример :-) Вот нужно знать точную причину, по которой не выполнилась транзакция в функции f(): сервер недоступен, переданы недопустимые данные, транзакция уже существует... :-) Каждая ситуация обрабатывается по-разному :-) Теперь представь, что от этой функции зависит ещё одна функция g(), которая тоже может не отработать по нескольким причинам :-) Теперь ты вызываешь g(), в которую ты должен будешь передать уже теперь 2 решателя для f() и для g()? :-) Ну удачи :-)

Можно, конечно, вернуть bool «нишмагла», как ты сказал, но это то же самое, как гасить исключения в деструкторах :-) Многих это устраивает, впрочем :-)

anonymous
()
Ответ на: комментарий от uuwaan

Похоже, вы заново придумали механизм исключений из языка Eiffel: https://www.eiffel.org/doc/solutions/Exception Mechanism

Там как раз секция rescue предназначена для попытки восстановиться (хотя и в качестве finally блока может использоваться).

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

А если тебе надо структурировать эту информацию в несколько полей? :-) Парсер писать изволишь, чтобы в catch им парсить?

Я не имел в виду только строку. Если строки от what() недостаточно, для своих исключений делаем базовый класс не std::exception, а myexception со всеми полями, необходимыми логгеру. А для чужих исключений, которые от myexception не наследуются, один фиг нужно писать код, который из их полей будет делать наши поля. Я бы сделал перегруженную функцию convert(their_exception) -> myexception.

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

Значит f() у нас будет принимать интерфейс ISolverForF с 3мя методами: onServerError(), onDataError(), onTransactionError(). Для удобства можно сделать класс, который с этим интерфейсом умеет хавать лямбды по одной на каждый случай, чтобы код не уносить далеко от вызова f(). Это если нам нужна какая-то нетривиальная реакция на эти ошибки. А если просто залогировать, то см выше: myexception со всем готовым и осознание факта, что f() не смогла.

Теперь представь, что от этой функции зависит ещё одна функция g()... Теперь ты вызываешь g(), в которую ты должен будешь передать уже теперь 2 решателя для f() и для g()?

g() принимает только решатель для g(). Те проблемы f(), которые g() может решить самостоятельно, она в интерфейс ISolverForG не выставляет. А те, которые решить не может, дублирует в SolverForG, делегируя их решение выше.

Количество обрабатывающего кода в принципе не меняется. В случае с исключениями было бы так:

main()
  bool didit;
  try {
    didit = g();
  }
  catch (g_exception1 from_g) {
  }
  catch (g_exception2 from_g) {
  }
  catch (f_exception1 not_handled_by_g) {
  }
  catch (f_exception2 not_handled_by_g) {
  }
  catch (// все остальные исключения, которые хотим логировать) {
    log_exception(get_log_info(e));
    didit = false;
  }
}

g() {
  try {
    f()
  }
  catch (f_exception3 handled_by_g) {
  }
  catch (f_exception4 handled_by_g) {
  }
}

f() {
  ...
  call_some_lib_fun();
  ...
}

А с коллбэками:

main() {
  SolverForG gs;
  gs.on_g_error1 = [](){...};
  gs.on_g_error2 = [](){...};
  gs.on_f_error1 = [](){...};
  gs.on_f_error2 = [](){...};

  bool didit;
  try {
    didit = g(gs);
  }
  catch(myexception e) {
  // "левые" исключения ловим по местам и конвертируем в myexception, throw myexception{get_log_info(e)};
    log_exception(e); 
    didit = false; 
  } 
} 

g() {
  SolverForF fs;
  fs.on_f_error1 = gs.on_f_error1;
  fs.on_f_error2 = gs.on_f_error2;
  fs.on_f_error3 = [](){...};
  fs.on_f_error4 = [](){...};

  bool didit = f(fs);
}

f() {
  ...
  try {
    call_some_lib_fun();
  } catch(// исключения от используемых не наших ф-ций, которые хотим логировать) {
    throw myexception{get_log_info(e)};
  } catch(// исключения от используемых не наших ф-ций, которые хотим обрабатывать) {
    call_appropriate_solver_method();
  }
}

Обрабатывающий и логирующий код прежний, только перераспределился по-другому. Стало видно из кода, обработку каких ситуаций каждая функция делегирует более высокому слою. Плюс наведён порядок: ситуации, которые мы намерены обрабатывать, мы обрабатываем не используя исключения. Исключения используем только чтобы залогировать нежданчик.

механизм исключений из языка Eiffel

Только там при указании retry сбойнувшая функция перезапускается целиком, а у нас выполнение продолжается с места сбоя.

uuwaan ★★
()
Последнее исправление: uuwaan (всего исправлений: 1)
Ответ на: комментарий от uuwaan

А для чужих исключений, которые от myexception не наследуются, один фиг нужно писать код, который из их полей будет делать наши поля. Я бы сделал перегруженную функцию convert(their_exception) -> myexception.

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

Значит f() у нас будет принимать интерфейс ISolverForF с 3мя методами
g() принимает только решатель для g(). Те проблемы f(), которые g() может решить самостоятельно, она в интерфейс ISolverForG не выставляет. А те, которые решить не может, дублирует в SolverForG, делегируя их решение выше.
В случае с исключениями было бы так:

Понятно, что ты придумал способ, но для конвертации исключений надо таки знать их все по типам, поэтому придётся лазить по всем зависимостям :-) Собственно об этом я и говорю :-)

anonymous
()
Ответ на: комментарий от anonymous

В которой ты перечислишь все типы исключений для конвертации без потери информации :-)

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

anonymous
()
Ответ на: комментарий от anonymous

поэтому придётся лазить по всем зависимостям

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

try {} catch (...) { throw myexception("f() не смогла") }
а потом по мере надобности втыкать более конкретные кэтчи выше всеядного.

uuwaan ★★
()
Последнее исправление: uuwaan (всего исправлений: 1)
Ответ на: комментарий от uuwaan

Я тут подумал, а зачем нам знать тип исключения?

хотя бы потому что исключение не обязательно означает ошибку.

dzidzitop ★★
()
Ответ на: комментарий от NeXTSTEP

Для деструкторов важен порядок их вызова (сначала для объекта, потом для его членов)

Мало того, важен порядок описания объектов в заголовке. Это для меня было культурным шоком.

Xintrea ★★★★★
()
Ответ на: комментарий от anonymous

Ты выдумываешь ограничения, которых нет и винишь в этом кого угодно кроме себя.

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

anonymous
()
Ответ на: комментарий от monk

А почему, думаешь, он до сих пор живой?

Я думаю, что это передёргивание.

А для крупных проектов язык с разбросом max/min больше чем C++ никуда не годится.

Тем не менее, С++ с «худшими показателями» для «крупных проектов» применяется чаще вижуал бейсика.

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

Expected не обязательно проверять
Таким образом мы получаем лучшее из двух миров обработки ошибок.

Ну нет. Option/Result тем и хороши, что проверять результат или игнорировать ошибку придётся сознательно и явно. С исключениями слишком легко и заманчиво их игнорировать: авось кто-то поймает и обработает уровнем выше.

Не хочу сказать, что исключения отстой, но это точно не лучше из двух миров. Максимум, «лучшее из двух миров, как его понимают любители исключений». (:

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Максимум, «лучшее из двух миров, как его понимают любители исключений». (:

Особенно в контекстах, где коды возврата убивают всю выразительность. Например, при перегрузке операторов. Скажем запись A=B*x+C, где B и C — это матрицы, а * и + — это перегруженные операторы.

eao197 ★★★★★
()
Ответ на: комментарий от monk

Ну то есть, примерно так же, если я всё правильно понял.

Ещё один вопрос из любопытства: а почему всё это добро лежит в ffi/unsafe?

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

Особенно в контекстах, где коды возврата убивают всю выразительность.

Очевидно, «коды возврата» плохо подходят для таких случаев. Именно поэтому я предпочёл бы в языке два полноценных механизма.

Надеюсь, моя мысль всё-таки была понята за желанием защитить исключения.

DarkEld3r ★★★★★
()
Ответ на: комментарий от Dudraug

Но в С++ по сути нам зарещают один из путей. Я лишь говорю, что это неправильно.

Проблемы эти «запреты» создают только если для их преодоления приходится делать что-то неудобное. Например, писать больше кода с потенциально большим количеством ошибок. Если же все «неудобства» возникают из-за непривычности, то это нормально. Разные языки - разные подходы.

Сторонники динамических языков многое в С# (ну и в С++ тем более) назовут неудобным, но это не значит, что надо судорожно «снимать исторические ограничения». Особенно, если это не ограничения, а вполне сознательные компромиссы.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Тем не менее, С++ с «худшими показателями» для «крупных проектов» применяется чаще вижуал бейсика.

Для бейсика хуже показатели, чем для C++. У бейсика max/min = 276/14 = 20, у C++ max/min 178/29 = 6.

Но для крупных проектов лучше C#, Javascript, J2EE, у которых max/min не превышает 2.

monk ★★★★★
()
Ответ на: комментарий от DarkEld3r

а почему всё это добро лежит в ffi/unsafe?

Потому что при работе с ffi оно всё надо почти всегда. А при нормальной работе всё или и так соберётся сборщиком мусора, или программист знает, что делает и напишет что-то вроде

(define my-will (make-will-executor))

(thread 
  (λ () 
    (let loop ()
      (with-handlers ([(lambda (e) #t) my-handler])
        (will-execute will))
      (loop)))))

(define (register-finalizer obj proc)
  (will-register my-will obj proc))

И будет иметь полный контроль над тем, в каком контексте происходит освобождение ресурсов.

monk ★★★★★
()
Ответ на: комментарий от eao197

Ох да, будто выразительность кода не убьёт другая крайность:

try {
    A = B * x + C;
} catch(...) {
    std::cerr << "Oops!\n";
}

Вопрос только в том, какие есть альтернативы? Хранить коды ошибок в объектах? Или вообще ставить везде и всюду ассерты?

kachsheev ★★★
()
Ответ на: комментарий от kachsheev

Ох да, будто выразительность кода не убьёт другая крайность:

«Выразительность кода» и «крайность» — это несовместимые вещи. Для любого подхода к обработке ошибок.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Например, при перегрузке операторов.

Вообще-то NaN и прочие не-числа придумали именно как «коды ошибок».

Ну будет у тебя {A, error?} = B*x+C Или A будет не матрицей, а «матрица-или-ошибка». В чём потеря выразительности?

monk ★★★★★
()
Ответ на: комментарий от monk

Мне бы очень хотелось попросить участников дискуссии включать мозги перед написанием ответов. Бреда здесь и так уже выплеснули предостаточно.

Если мозги таки включить и подумать, то основные проблемы не в том, будет ли A матрицей или «матрицей-или-ошибкой», а в том, допустимо ли выполнять B*x, что будет результатом B*x, допустимо ли складывать B*x и С, и т.д.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

A матрицей или «матрицей-или-ошибкой», а в том, допустимо ли выполнять B*x, что будет результатом B*x, допустимо ли складывать B*x и С, и т.д.

А в чем у тебя проблема? Обернул в монады и вперед. Если где-то в выражении случится ошибка, на выходе получишь эту ошибку.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.