Здравствуйте, jahr, Вы писали:
J>Как раз другие варианты и хотелось бы найти.)
Ну а какие еще могут быть варианты, если система возвращает ошибку, которую проигнорировать нельзя, а обработать невозможно?
Здравствуйте, jahr, Вы писали:
Q>>Запись в файл делай в отдельном методе Save(), или Flush(), или Close(), etc. В коде явно вызывай этот метод (потенциально бросающий исключение) в нормальном потоке выполнения, не полагаясь на деструктор. Q>>В деструкторе тоже можешь попытаться вызвать этот метод или его содержимое (проверив, что объект «грязный» и сохранение действительно нужно, и не было ранее сделано вызовом Save()), но только обеспечив непросачивание исключений из этого вызова. Но лишь как last resort, не полагаясь на это при нормальном использовании класса.
J>Тогда мне придется каждое использование этого класса обкладывать try'ем, в catch'е которого вызывать этот метод Save, использование класса станет мучением, эти перехваты везде писать будет лениво, я начну это пропускать, начнутся ошибки и понесется.) Тогда уж проще вообще отказаться от исключений и работать только на кодах возврата как в С.)
Но методы flush и close у стандартного fstream примерно так и работают. fstream делает попытку «сохранить» (сбросить буффер) в деструкторе, но никак не уведомляют в случае фейла. При ручном закрытии успешность close'а можно опросить (badbit, failbit).
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Тяжёлые операции в конструкторе — без проблем.
ага, знаем мы эти "без проблем" в случае статиков и глобалов..
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, landerhigh, Вы писали:
J>>Как раз другие варианты и хотелось бы найти.) L>Ну а какие еще могут быть варианты, если система возвращает ошибку, которую проигнорировать нельзя, а обработать невозможно?
почему это?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Деструкторы могли бы нормально кидать исключения, например если бы многократные исключения аккумулировались в одно. EP>В случаях же где из-за алгоритмических особенностей нужен не кидающий деструткор — использовали бы noexcept.
EP>Даже была статья на эту тему — "Destructors That Throw: Evil, or Just Misunderstood? — Jon Kalb and Dave Abrahams" (оригинальный сайт выпилен, а копию/кэш я сходу не нашёл). Там была фраза, что-то в духе: "делать std::terminate в таких случаях это слишком драконовские меры".
Почитал. Принципиальной невозможности аккумулировать исключения имхо особенно нет. Но у меня другой вопрос — а что с объектом-то? Вот выкинул он исключение на полпути деструктора. И стал зомби. Вернуть к жизни его нельзя. Добить? Как? Разрешать рекуррентный вызов деструктора, чтобы наверняка добить? Так это ничем не отличается от цепочки cleanup() — delete.
Или принять ограничения, что исключение в деструкторе — чисто информационное и объект, выбросивший его, гаратированно убит?
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, landerhigh, Вы писали:
J>>>Как раз другие варианты и хотелось бы найти.) L>>Ну а какие еще могут быть варианты, если система возвращает ошибку, которую проигнорировать нельзя, а обработать невозможно? V>почему это?
У меня есть смутное подозрение, что этот разговор опять смещается в сторону сферического случая в вакууме с обработкой ошибки от функции закрытия файла.
Здравствуйте, jahr, Вы писали:
J>Много лет не могу для себя решить, как правильно себя вести при возникновении каких-то проблем во время выполнения деструктора.
J>Например, для конкретики, есть у нас класс, в конструкторе читает данные из файла, в деструкторе — записывает модифицированные данные обратно в файл. Делать все это хочется именно в конструкторе и деструкторе, чтобы эти действия выполнялись автоматически, при возникновении исключений при выполнении промежуточных действий — они правильно обрабатывались и т.п., как мне кажется, это стандартная более-менее практика.
J>И вот что делать, если в деструкторе не удалось выполнить запись в файл? Исключение кидать нельзя, игнорировать ошибку как-то неправильно, запись в лог — какое-то костыльное решение, не всегда уместно. J>Не выполнять в деструкторе никаких сложных действий? Но как же тогда концепция автоматического захвата-освобождения ресурсов в конструкторе-деструкторе, она вроде как считается правильной, и мой пример работы с файлом в нее укладывается, насколько я ее понимаю.
J>В общем, буду признателен, если вы поделитесь своими способами реализовать описанный в примере сценарий работы идеологически правильным способом.)
Нормального — идеологически — варианта нет.
Можно использовать (как большинство и делает) "грязный" вариант с глушением ошибок в деструкторе, и надеяться, что ошибка не произойдет. В принципе, в подавляющем большинстве случаев она и не происходит.
Если же нужна обработка, то только отдельный метод для финализации. Более многословно, но такой подход и более корректен.
Здравствуйте, landerhigh, Вы писали:
L>>>Ну а какие еще могут быть варианты, если система возвращает ошибку, которую проигнорировать нельзя, а обработать невозможно? V>>почему это? L>У меня есть смутное подозрение, что этот разговор опять смещается в сторону сферического случая в вакууме с обработкой ошибки от функции закрытия файла.
я не понял почему ошибку проигнорировать нельзя? кто сказал что нельзя?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, jahr, Вы писали:
J>Тогда мне придется каждое использование этого класса обкладывать try'ем, в catch'е которого вызывать этот метод Save, использование класса станет мучением, эти перехваты везде писать будет лениво, я начну это пропускать, начнутся ошибки и понесется.) Тогда уж проще вообще отказаться от исключений и работать только на кодах возврата как в С.)
Учитывая, что обработка в catch это очень затратная процедура, то проверка в кодах возрата,
как в Си, будет явно производительней, за исключением случаев, когда проверяемая функция вызывается
в циклах с миллионами итераций.
Здравствуйте, Vain, Вы писали:
L>>>>Ну а какие еще могут быть варианты, если система возвращает ошибку, которую проигнорировать нельзя, а обработать невозможно? V>>>почему это? L>>У меня есть смутное подозрение, что этот разговор опять смещается в сторону сферического случая в вакууме с обработкой ошибки от функции закрытия файла. V>я не понял почему ошибку проигнорировать нельзя? кто сказал что нельзя?
Ключевое слово выделено вверху. Это то, о чем ТС спрашивал.
Здравствуйте, landerhigh, Вы писали:
V>>я не понял почему ошибку проигнорировать нельзя? кто сказал что нельзя? L>Ключевое слово выделено вверху. Это то, о чем ТС спрашивал.
система вроде вообще не может вернуть ошибку, которую нельзя проигнорировать, для чего тогда хуки?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
L>>Ключевое слово выделено вверху. Это то, о чем ТС спрашивал. V>система вроде вообще не может вернуть ошибку, которую нельзя проигнорировать, для чего тогда хуки?
Здравствуйте, jahr, Вы писали:
J>И вот что делать, если в деструкторе не удалось выполнить запись в файл? Исключение кидать нельзя, игнорировать ошибку как-то неправильно, запись в лог — какое-то костыльное решение, не всегда уместно. J>Не выполнять в деструкторе никаких сложных действий? Но как же тогда концепция автоматического захвата-освобождения ресурсов в конструкторе-деструкторе, она вроде как считается правильной, и мой пример работы с файлом в нее укладывается, насколько я ее понимаю.
А можешь расписать алгоритм, как в идеале ты это видишь?
У меня для приближения к идеалу тут 2 перпендикулярных пункта:
1. вынести всю работу с файлом в отдельный класс, который и будет заниматься разными ситуациями в своём деструкторе. Это проблему не решит, но наведёт больше порядка и поделит проблему надвое.
2. если нужна ситуация, которую надо обработать _не_ в конструкторе/деструкторе, при этом хочется использовать этот механизм, то я бы завёл некий класс-няньку, который в конструкторе создавал бы объект моего класса, или принимал ссылку на него и тогда ничего не делал. А в деструкторе звал бы release или что там нужно для освобождения. При этом, да, нужно где-то хранить ещё указатель на сам объект нашего класса(можно завести пул объектов, куда смотрят няньки). В результате:
2.1. в нянька-конструкторе, если что-то пошло не так, мы просто не получим наш объект. Всё ок.
2.2. в нянька-деструкторе, если что-то пошло не так, объект-нянька погибнет, но сможет сообщить, что освобождение ресурсов не завершено, или просто сбросит свой дочерний объект в пул. Указатель на наш объект остался жив, мы можем создать новый объект-няньку, которому передать ссылку на наш объект и повторить пункт 2.2.
коряво, да, но в _нашем_ классе проблема решена но это всё, imho, "вам шашечки или ехать?", кому что больше хочется.
если конкретно про файл, то мне кажется, ситуацию с завершением можно решить в деструкторе нормально, т.е. записать в файл, если не записалось, записать в другой, или в лог, или окно показать или ещё что, в зависимости от ситуации и того, что хотел автор.
Здравствуйте, CEMb,
CEM>2.2. [.....] Указатель на наш объект остался жив, мы можем создать новый объект-няньку, которому передать ссылку на наш объект и повторить пункт 2.2.
Мне представляется, что тут самой главной проблемой будет — как корректно "добить" наш объект, учитывая, что он уже частично "мертвый", частично еще "живой"? ("А что недострелили — так я не виноват." (с))
Здравствуйте, Vlad_SP, Вы писали:
CEM>>2.2. [.....] Указатель на наш объект остался жив, мы можем создать новый объект-няньку, которому передать ссылку на наш объект и повторить пункт 2.2.
V_S>Мне представляется, что тут самой главной проблемой будет — как корректно "добить" наш объект, учитывая, что он уже частично "мертвый", частично еще "живой"? ("А что недострелили — так я не виноват." (с))
Ага, вот потому и пункт 1 — минимизировать классы таких объектов. Т.е. или жив или мёртв(отсутствует в пуле). Если жив, убивается ровно одним однозначным способом. К примеру, файл полностью записан и закрыт.
Здравствуйте, CEMb, Вы писали:
CEM> К примеру, файл полностью записан и закрыт.
В файл записалась только половина данных, дальше кончилось место на диске.
На сервер в Тимбукту передана только половина данных, дальше пьяный экскаваторщик Вася в Мухосранске порвал магистральный кабель.
И так далее....
Здравствуйте, Vlad_SP, Вы писали:
V_S>В файл записалась только половина данных, дальше кончилось место на диске. V_S>На сервер в Тимбукту передана только половина данных, дальше пьяный экскаваторщик Вася в Мухосранске порвал магистральный кабель. V_S>И так далее....
Всем такие события поздновато разруливать в деструкторе.
В принципе можно, но под этим деструктором должна быть еще куча уровней, которые умеют это разруливать сами.
Здравствуйте, landerhigh, Вы писали:
EP>>Даже была статья на эту тему — "Destructors That Throw: Evil, or Just Misunderstood? — Jon Kalb and Dave Abrahams" (оригинальный сайт выпилен, а копию/кэш я сходу не нашёл). Там была фраза, что-то в духе: "делать std::terminate в таких случаях это слишком драконовские меры". L>Почитал. Принципиальной невозможности аккумулировать исключения имхо особенно нет.
Альтернативный вариант, озвученный в статье, это игнорировать все кроме первого исключения.
Думаю можно вызывать глобальный пользовательский callback для игнорируемых исключений.
L>Но у меня другой вопрос — а что с объектом-то? Вот выкинул он исключение на полпути деструктора. И стал зомби. Вернуть к жизни его нельзя. Добить? Как? Разрешать рекуррентный вызов деструктора, чтобы наверняка добить? Так это ничем не отличается от цепочки cleanup() — delete. L>Или принять ограничения, что исключение в деструкторе — чисто информационное и объект, выбросивший его, гаратированно убит?
Например в само исключение можно запаковывать все внутренности объекта. То есть например file handle будет внутри объекта исключения.