LINUX.ORG.RU

Success Exception? WTF?

 , ,


0

0

Здравствуйте мои дорогие, великие программисты, бакалавры и прочие ЛОРовцы включая умнейших анонимусов!

А вот что если я вам скажу следующее:

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

И вот есть код, в котором, где-то в дебрях, при определённых условиях требуется:

а) вывалить сообщение и сделать exit(0);

б) сделать exit(0) без каких либо сообщений вообще.

Приведу простой пример: программа выполняется по крону, и нужно, в случае штатного завершения, не засирать логи кронтаба и не слать емейлы тому, от чьего имени эта программа по крону запускается. Вот тут и пригождается кейс «б» — код завершения есть, а сообщения нет.

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

И теперь у меня есть «SuccessException(message)» и, конечно, «JustSuccessExit extends SuccessException»

А что вы думаете по этому поводу?

UPD: ПОЖАЛУЙСТА, ПРЕЖДЕ ЧЕМ НАПИСАТЬ СООБЩЕНИЕ — ПРОЧТИТЕ ВЕСЬ ТРЕД!!! СКОРЕЕ ВСЕГО ВАШ ВОПРОС ИЛИ ВАША ПРЕТЕНЗИЯ УЖЕ ОБСУЖДАЛАСЬ!

★★★★★

Последнее исправление: deep-purple (всего исправлений: 2)
Ответ на: комментарий от PRN

С исключениями еще короче:

Их не во всех библиотеках/фреймворках можно использовать. CheckRet спокойно интегрируется в любую систему основанную на кодах ошибок.

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

Починил:

void insert_XY(Transaction & t)
{
	// Ожидаем вставку и X, и Y
	try {
		insert_X(t);
	} catch (SuccessException &e) {
		insert_Y(t);
	}
	throw ExceptionMissingException;
}

Но это конечно говнокод.

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

AppException еще и решает

Специально для вас — это тупо структура с полями. И без заморочек! Поэтому он ничего не решает, а только переносит до нужного места. Ну давайте отнаследуем всё что бросается от общего родителя — будет тот же эфект. Мне какой из вариантов показать кодом? Может быть все возможные? Ну так, чтобы дошло. Вам вот лениво мозгами раскинуть, а мне вот должно быть не лениво писать все варианты? Как для дитёнка прям. С подробнейшими комментариями. Вот тут скобочку поставим, ато канпилятиръ заругаицца! А вот тут бросим объектик, да такой, что он у нас тупенький. По настоящему тупенький, ты, сынка, не думай про него всякого — обидели его создатели, вот и тупенький он.

Зачем выносить в конструктор исключения логгирование, обработку ошибок, etc?

Ни в какой конструктор никакого объекта исключения я логгирование и прочую фигню не вносил. Перечитайте.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от X512

Их не во всех библиотеках/фреймворках можно использовать

Там где они есть можно не упарываться)

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

Своим примером

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

тебе лень написать нормальный workflow

И норма это вычитка из шпаргалок для макак?

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от Marvel

У ТСа приложение просто выходит.

П.С. А так, пример почему SuccessException юзать не надо хорош.

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

Потому что для этого придумали goto.

Kill it with FIRE!!!

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

А потом попробуй разбери эту лапшу из goto. О внесении изменений страшно задумываться. Для решения этой проблемы давно придумали RAII.

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

Не принимается код ошибки. Иногда важно передать конкретный код ошибки, а не просто факт ошибки.

Так конкретные коды ошибок и передаются. Это 32-битное знаковое число, в котором участок бит отведен по severity ошибки, часть под код подсистемы (facility), и часть под код конкретной ошибки (code). Глобально, упрощенный обработчик кодов работает по принципу res == 0 - всё совсем хорошо, res < 0 - ошибка, res > 0 - warning.

Детально - выкусывает секции битиков и лезет в несколько ассоциативных массивов, если вдруг захотелось из кода возврата сделать человеко-читаемое сообщение вроде «Статус» «Подсистема» «Что произошло».

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

Немного неудобно во время добавления новых кодов состояний, но отладка например проходит приятно.

nikitos ★★★
()
Ответ на: комментарий от ya-betmen

Ну так то, ты верно говоришь. В таком случае этот ПилоткаЗаперетьСемафор надо отнаследовать от этого базового ДжастЭксит.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

Ни в какой конструктор никакого объекта исключения я логгирование и прочую фигню не вносил. Перечитайте.

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

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

[Нет. Он тупой и просто принимает контекст. Решение принимается с месте отлова, в данном примере только установкой актуального кода возврата.] Но можно добавить всякого

Можно. Зачем? Локальную ошибку обработайте локально, либо вообще не ловите, пусть раскручивается до глобального обработчика. Зачем выносить в конструктор исключения логгирование, обработку ошибок, etc?

Так нагляднее?

Специально для вас — это тупо структура с полями.

Тогда ### просветите меня, как же из context самозарождается значение, возвращаемое AppException::retCode(). Не иначе, rand() возвращается. Напомню, именно то, что вернет AppException::retCode(), и будет показателем успешности исполнения функции.

…А вот тут бросим объектик, да такой, что он у нас тупенький. …

Проспитесь, вы позорите свое же имя. Я прошу вас конкретно обозначить ваши тезисы и начать наконец писать по делу. Вы приводите убогие примеры, и никак не можете уловить мысль, которую я вам пытаюсь скормить с ложечки уже несколько комментариев подряд. @ya-betmen о том же говорит, кстати. И, как ни странно, все без толку, вы продолжаете переходить на личности и съезжать с темы.

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

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

Чем хороший код отличается от говнокода? В первую очередь тем, что в хорошем коде соблюдаются определённые гарантии, а в говнокоде их нет. Например, «функция делает задачу в соответствии со своим именем», «принимает только те аргументы, которые ей нужны», «имена переменных отражают своё назначение», «имена i и j используются для хранения индексов», «писать комментарии на английском языке» и т.д.

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

Marvel
()
Последнее исправление: Marvel (всего исправлений: 2)
Ответ на: комментарий от X512

Я был бы готов с вами согласиться, если бы речь шла только про плюсовый код с большим числом RAII. Однако когда я хочу получить кроссплатформенный ассемблер, я буду писать как на кроссплатформенном ассемблере, а там без goto делать нечего.

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

Однако когда я хочу получить кроссплатформенный ассемблер

C++ и RAII есть везде включая актуальные микроконтроллеры. Это должны быть какие-то совсем копролиты чтобы ничего кроме Си не было.

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

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

Мы, кстати, отошли от темы. Я считаю for(;;){ break; } бессмысленным, потому что в С++ для этой цели есть исключения (и тогда весь разговор не имеет смысла), а если разговаривать про С (или участки кода, где исключения недопустимы), то в наличии goto в таких местах ничего зазорного нет.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium
int retCode = 0;

try {

    // тут можно бросить AppException
    // если что-то глобально не так

    try {

        // тут вообще может быть много чего
        // и по разному
        // но в этом примере только про семафор и БД

    } catch (LockFailed) {
        throw AppException(LockFailed->context());
    } catch (DBFailed) {
        throw AppException(DBFailed->context());
    }

    // тут можно бросить AppException
    // если что-то глобально не так

} catch (AppException) {

    // логгирование будет вот тут
    // а не в конструкторе AppException
    // причем логгировать или нет
    // зависит как от контекста в целом
    // так и от статуса в этом контексте в частности

    // локальные ошибки обрабатываются локально
    // и к этому месту никакого отношения не имеют

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

    // тут статус преобразуется в код возврата
    // для простоты понимания - напрямую

    retCode = AppException->retCode();
}

return retCode;

Я прошу вас конкретно обозначить ваши тезисы

Выдал выше.

ваши убогие примеры

Ваши так вообще не в попад и не по делу. Но, может не будем эту ветвь развивать?

мысль, которую я вам пытаюсь скормить с ложечки

Вообще не вижу. Может проще сказать напрямую? Я прямоту кстати люблю. Даже если она меня и заденет. Так что настаиваю.

ya-betmen

Ну не зная о чем вы, не могу утверждать что он совсем о другом.

вы продолжаете переходить на личности

Прошу прощения. С тестем уже махнул пару рюмашек коньячку. Тестя уважить — святое дело. Даже не смотря на то что я так то не пью — гипертония. Просто он редко пьёт, а сегодня действительно есть повод.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от Marvel

функция делает задачу в соответствии со своим именем

Это типа такой намёк на «иксепшн всегда бросается только в случае ошибки»? Ахахаха — обсуждено на первой странице. В мою пользу.

большинство программистов ожидают от кода

А кто сказал что я в комментарии не напишу, что тут иначе чем ты думаешь?

никакой заметной выгоды

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

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

А зачем там вложенный трай? Можно же проще

try {
} catch (LockFailed) {
  // Логика с LockFailed->context()
  // retCode = что-то
  // ...
} catch (DBFailed) {
  // Логика с DBFailed->context()
  // retCode = что-то
  // ...
} catch (const AppException&) {
}

ya-betmen, кажется, писал о том что у исключение название не отображает его суть (когда оно кидается)

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

а если разговаривать про С, то в наличии goto в таких местах ничего зазорного нет.

И setjmp, longjmp через которые реализованы многие библиотеки исключений для С, где эмулируются try, catch, finally. Хотя мне как-то уже и прямое использование setjmp, longjmp кажется более читабельным, чем какие-то библиотеки у которых могут быть свои особенности…

C слишком мал, чтобы выпиливать оттуда что-то типа макросов, goto или setjmp, longjmp

Это в С++ можно выбирать лишь подмножества С++ и всё равно будет очень много всего…

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 2)
Ответ на: комментарий от deep-purple

Выражаю мысль кратко: вам нужно ExitSuccess переименовать в обычный LockFailed или что-то в этом духе. Все.

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

Про логгирование понял, вы говорили о нем в контексте рассуждений о происходящем в конструкторе AppException, поэтому я отнес это туда.

За комментарии в примере спасибо, стало понятнее: так, из приведенного вами изначально примера следовало, что после catch (DBFailed) { ... } у вас больше кода в этом скоупе нет, что (в том числе) меня запутало.

Siborgium ★★★★★
()
Ответ на: комментарий от deep-purple

По идее +/- тоже самое что и

    } catch (LockFailed) {
        throw AppException(LockFailed->context());
    } catch (DBFailed) {
        throw AppException(DBFailed->context());
    }

только как-то так

} catch (LockFailed) {
  retCode = Do(LockFailed->context())
} catch (DBFailed) {
  retCode = Do(DBFailed->context())
}

Но оно в общем блоке кетчей, не размазанное по коду. К тому же исключение 2 раза не кидается, хотя в данном контексте перформанс уже не важен.

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

Да, это именно то, что я пытался донести, говоря про принятие решения в конструкторе AppException. Тем не менее, я могу допустить, что при наличии дополнительного кода после этих catch на основе retCode будут приниматься какие-то решения, так что ладно уж.

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

Ну нам как бы плевать. А что ты думаешь по этому поводу?

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

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

нужно ExitSuccess переименовать в обычный LockFailed

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

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

Соглашусь с EXL, нафига тебе стек, если ты с ним ничего не делаешь и просто выходишь? Не нужен такой эксепшон, просто вы-хо-ди, нефиг собирать трейс который нигде не будет вывален.

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

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

В чем ее исключительность? Я (и, полагаю, не только я) понимаю термин «исключительный» как антоним «штатного».

И эта ситуация вся насквозь штатная — приложение успешно сделало, что от него требовалось, и должно завершиться. Зачем наворачивать какие-то неочевидные механизмы, которые затруднят понимание твоих намерений? Мне насрать на всех, пишу как хочу? Или… пишу как умею? %)

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

а другой неудавшийся лок уже будет ошибкой

Для решения этой проблемы я и предлагаю переименование. Я согласен, LockFailed будет пересекаться с обычной ошибкой, тогда имеет смысл переименовать в что-то вроде InstanceAlreadyExists, или что-то в этом духе. Главное – уйти от ExitSuccess, которое несет в себе не тот смысл, который вы в него хотите заложить.

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

У него в этих catch (catch (LockFailed)) просто другое исключение кидается, чтобы перейти в catch (AppException), код за ними не будет выполнен.

PRN
()
Ответ на: комментарий от deep-purple

Разве свобода мысли не в том, чтобы делать, а потом думать, вопреки тому, чтобы думать, а потом делать?

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

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

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

Имелся в виду не код, который выполнится после этого catch, а код, который располагается непосредственно ниже catch, и выполнится при успешном выполнении try. Там, где комментарий написан.

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

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

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

высвобождены корректно ты хочешь сказать, или в принципе ты готов придумать такой ресурс который не будет отпущен при exit(0)?

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

Мне не нужно «придумывать». Простой пример такого ресурса – лок-файл. Без вызова unlink или аналогов будет закрыт только хэндл к файлу, но сам файл удален из файловой системы не будет.

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

следить за порядком кетчей, при развесистой иерархии

Нет — я буду ловить базовый, т.е. туда попадут все его наследники.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от anonymous

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

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

ресурс который не будет отпущен при exit(0)

Вопрос exit(0) это частный случай. Я же говорю за общее место обработки любой фигни, которая туда попала. Обрати внимание — в моем примере написано exit(retCode); т.е. это контекст решает какой код возврата будет. И решает еще много чего кроме кода возврата. Такая себе местечковая стейт машина.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

А если они требуют разных действий?

    } catch (const LockFailed&) {
      // Do(LockFailed->context())
    } catch (const кантлоксемафор&) {
      // exit(0)
    }

При наследовании второго от первого, кантлоксемафор никогда не обработается.

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

так работает очень много ПО файл временный он умрет между перезапусками системы если его просто не удалят. Давай еще пример.

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

Это же зависит от реализации. Вернее так: надо продумать вот этот базовый трай кеч блок и продумать несколько вложенных если они предполагаются. Ну и естественно насоздавать нужных типов которые будут бросаться. А после этого всё станет «конечным автоматом» — только бросай нужное.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Он умрет между перезапуском системы. А не между перезапуском приложения. Кстати то же самое с блокировкой файла. Если приложение при выходе не разблокировало его, то вновь запустившись обнаружит что он всё еще заблокирован. Ой!

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

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

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