Legalize throwing destructors! D's scope(failure) and scope(success) in C++
От: Evgeny.Panasyuk Россия  
Дата: 27.09.12 19:02
Оценка: 194 (8)
stack_unwinding это маленькая header-only библиотека, которая реализует примитив(class unwinding_indicator) позволяющий определить, был ли вызван деструктор объекта из-за раскрутки стэка или "нормальным" образом

Библиотека позволяет определить в каких случаях реально опасно кидать исключение из деструктора. То есть когда исключение покинувшее деструктор может привести к вызову std::terminate.
В результате, возможно достичь такого же эффекта, как ручная расстановка ".close()" в конце блока, автоматически.
{
   File a,b;
   // ...
   b.close(); // may throw
   a.close(); // may throw
}

станет
{
   File a,b;
   // ...
}

В более общем случае, это поможет созданию "продвинутого" Scope Guard, который не требует ручного вызова commit/release, а также "понимает" исключения из деструкторов.
Язык D имеет scope(success) и scope(failure), которые позволяют задать отложенное действие на конец блока, которое выполняются(либо нет) в зависимости от того, произошёл ли выход из блока по исключению или обычным образом.

В этой библиотеке есть примеры реализаций "scope(success)" и "scope(failure)" (C++03, без лямбд):
{
    cout << "Case #1: stack unwinding" << endl;
    scope_action exit=make<scope_exit>(Print(" exit"));
    scope_action failure=make<scope_failure>(Print(" failure")); 
    scope_action success=make<scope_success>(Print(" success"));
    throw 1;
}


В Boost есть библиотека "Scope Exit", которая в примерах использует решение похожее на ручной вызов release/commit (там используется bool переменная).
Цитата из мануала Boost.ScopeExit:

Boost.ScopeExit is similar to scope(exit) feature built into the D programming language.

A curious reader may notice that the library does not implement scope(success) and scope(failure) of the D language.
Unfortunately, these are not possible in C++ because failure or success conditions cannot be determined by calling std::uncaught_exception (see Guru of the Week #47 for details about std::uncaught_exception and if it has any good use at all).
However, this is not a big problem because these two D's constructs can be expressed in terms of scope(exit) and a bool commit variable (similarly to some examples presented in the Tutorial section).


Работоспособность библиотеки была проверенна на: {MSVC2005,MSVC2008,MSVC2010,MSVC2012,GCC 4.1.2,Clang 3.2}x{x32,x64}x{дефолтные настройки}

На данный момент, библиотека реализована поверх платформо-зависимой реализации функции uncaught_exception_count.
uncaught_exception_count — это функция подобная std::uncaught_exception из стандартной библиотеки, но вместо булевского результата возвращает unsigned int, показывающий текущее количество uncaught exceptions

Ссылки:
* http://www.boost.org/doc/libs/1_51_0/libs/scope_exit/doc/html/index.html
* http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D
* http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758
* http://www.gotw.ca/gotw/047.htm
* https://github.com/panaseleus/stack_unwinding
Re: Legalize throwing destructors! D's scope(failure) and scope(success) in C++
От: PlusMyTwitterFace  
Дата: 27.09.12 20:56
Оценка:
EP>Библиотека позволяет определить в каких случаях реально опасно кидать исключение из деструктора. То есть когда исключение покинувшее деструктор может привести к вызову std::terminate.

std::uncaught_exception()?

С другой стороны, это C++11-only.
Re[2]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 27.09.12 21:03
Оценка:
Здравствуйте, PlusMyTwitterFace, Вы писали:

EP>>Библиотека позволяет определить в каких случаях реально опасно кидать исключение из деструктора. То есть когда исключение покинувшее деструктор может привести к вызову std::terminate.

PMT>std::uncaught_exception()?

На данный момент, библиотека реализована поверх платформо-зависимой реализации функции uncaught_exception_count.
uncaught_exception_count — это функция подобная std::uncaught_exception из стандартной библиотеки, но вместо булевского результата возвращает unsigned int, показывающий текущее количество uncaught exceptions


есть случаи в которых std::uncaught_exception() возвращает true, но при этом исключение можно кидать, и scope_success должен быть вызван (то есть scope покидается не по исключению).

PMT>С другой стороны, это C++11-only.


std::uncaught_exception — C++98
Re: Legalize throwing destructors! D's scope(failure) and scope(success) in C++
От: _NN_ www.nemerleweb.com
Дата: 28.09.12 05:02
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

Круто !
Как-то давно была нужна фишка с scope(success) , в итоге пришлось через флаг делать.

Хотелось бы поддержку C++11 с перемещаемыми объектами.
Сейчас Action должен быть копируемым, что не всегда будет радовать.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Legalize throwing destructors! D's scope(failure) and scope(success) in C++
От: rg45 СССР  
Дата: 28.09.12 07:25
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>stack_unwinding это маленькая header-only библиотека, которая реализует примитив(class unwinding_indicator) позволяющий определить, был ли вызван деструктор объекта из-за раскрутки стэка или "нормальным" образом


EP>Библиотека позволяет определить в каких случаях реально опасно кидать исключение из деструктора. То есть когда исключение покинувшее деструктор может привести к вызову std::terminate.


Гм... а вот, например, при "нормальном" вызове деструкторов элементов массива опасно бросать исключения или нет?
--
Re: Legalize throwing destructors! D's scope(failure) and scope(success) in C++
От: UniqueRSDN  
Дата: 28.09.12 07:45
Оценка: +3
EP>Библиотека позволяет определить в каких случаях реально опасно кидать исключение из деструктора. То есть когда исключение покинувшее деструктор может привести к вызову std::terminate.
EP>В результате, возможно достичь такого же эффекта, как ручная расстановка ".close()" в конце блока, автоматически.

Как быть с ситуациями, когда вызывающий деструктор код рассчитывает на то, что из деструктора не полетит исключение?
Тот же Саттер, рассуждая о безопасности исключений приходит в выводу, что полной безопасности не получится достичь,
не имея гарантии отсутствия исключений для некоторых функций. Деструктор входит в число этих функций.
Ну или более конкретно, как разрулится такая ситуация:
есть непустой стандартный контейнер объектов. он вышел из скоупа и позвался его деструктор.
он начал звать деструкторы у содержащихся в нем объектов, которые делают важные вещи, правильно распознают
контекст вызова деструктора (по исключению или нет) и если не по исключению, то могут бросить его.
допустим исключение бросил первый же деструктор объекта. деструкторы остальных объектов не позвались.
в результате имеем утечку. или там это как-то хитро разруливается?
Re[2]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 28.09.12 07:46
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Хотелось бы поддержку C++11 с перемещаемыми объектами.

_NN>Сейчас Action должен быть копируемым, что не всегда будет радовать.

да, надо будет использовать boost::move (при наличии boost), или std::move при его наличии..
Re[2]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 28.09.12 09:00
Оценка:
Здравствуйте, rg45, Вы писали:

R>Гм... а вот, например, при "нормальном" вызове деструкторов элементов массива опасно бросать исключения или нет?


какого именно массива? File f[2]? или delete [] new File[2] ?
На File f[2] индикатор сейчас работает неправильно — current_exception_count изменяется уже после удаления всего массива(по крайней мере на MSVC).
Есть и другие ограничения. Например когда объект "плавает", то есть создаётся при stack unwinding, но переживает раскрутку(например в глобальном объекте)
Надо добавить секцию limitations..
Re[3]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: rg45 СССР  
Дата: 28.09.12 10:21
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


R>>Гм... а вот, например, при "нормальном" вызове деструкторов элементов массива опасно бросать исключения или нет?


EP>какого именно массива? File f[2]? или delete [] new File[2] ?

EP>На File f[2] индикатор сейчас работает неправильно — current_exception_count изменяется уже после удаления всего массива(по крайней мере на MSVC).
EP>Есть и другие ограничения. Например когда объект "плавает", то есть создаётся при stack unwinding, но переживает раскрутку(например в глобальном объекте)
EP>Надо добавить секцию limitations..

Я имел ввиду, что при выбрасывании исключения из деструктора какого-либо элемента массива, деструкторы остальных элементов просто не будут вызваны, со всеми вытекающими. И никаким счетчиком проблема не решается, как бы он ни работал. Это актуально вне зависимости от области хранения массива и к стандартным контейнерам все сказанное относится в равной мере.
--
Re[2]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 28.09.12 11:30
Оценка:
Здравствуйте, UniqueRSDN, Вы писали:

URS>Как быть с ситуациями, когда вызывающий деструктор код рассчитывает на то, что из деструктора не полетит исключение?


Я думал над этим — проблема с транзитивностью действительно есть. Если объект A использует B, который вдруг начал использовать C кидающий исключения из деструктора — то теперь и B и A, и все кто их агрегирует/наследуют, кидают исключения из деструкторов. Причём есть много кода, который не насчитан на кидающие деструкторы.
Похожая проблема существует с мутабельностью.

Сейчас, безопасно(?) можно использовать объект с кидающими деструкторами в блоке кода, но не как член или родитель класса. Например можно кидать исключения в action у scope(success).

URS>Тот же Саттер, рассуждая о безопасности исключений приходит в выводу, что полной безопасности не получится достичь,

URS>не имея гарантии отсутствия исключений для некоторых функций. Деструктор входит в число этих функций.

По поводу кидающих деструкторов, недавно вышла такая статья: Evil, or Just Misunderstood?

URS>Ну или более конкретно, как разрулится такая ситуация:

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

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

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

Например, текущая ситуация по-умолчанию(без явных вызовов flush/close/etc, без логирования фэйла(которое может также сфэйлится)):

1. Первый деструктор — фейл, no action
2. Второй деструктор — фейл, no action
3. Обработка молча завершена

или с киданием исключения по возможности:

1. Первый деструктор — фейл, кидает исключение
2. Второй деструктор во время раскрутки — фэйл, кидать нельзя
3. catch первого исключения выше по call-stack'у, в месте ответственном за их обработку
4. информирование пользователя
5. пользователь устраняет причины, и запускает обработку заново
6. Первый деструктор — success
7. Второй деструктор — фэйл (а может и не фэйл, если зависело от первого), кидает исключение
... пользователь узнаёт об этом, исправляет и запускает заново.
Ведь вполне возможный сценарий.

Что лучше?
Re[3]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: UniqueRSDN  
Дата: 28.09.12 12:02
Оценка: 13 (2) +4
я не знаю что лучше. сложно все как-то получается.
можно придумать кейс, в котором запуск по новой фейлится еще раньше, чем первый фейл, потому что деструкторы не отработали до конца.
пока что все выглядит так, что для того, чтобы все корректно работало с кидающимися деструкторами, нужно специальным образом
писать код, учитывающий факт бросания исключений из деструкторов. в случае не кидающихся деструкторов код тоже нужно правильно
организовать. вопрос в том, что будет проще. пока что особого профита я не увидел и предпочту вариант, в котором деструкторы
все-таки не кидают ничего. эта дорожка хотябы протоптана.
Re[4]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 28.09.12 12:11
Оценка:
Здравствуйте, rg45, Вы писали:

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


#include <iostream>
#include <exception>
using namespace std;
 
struct T
{
    ~T()
    {
        cout << "~T()";
        throw 1;
    }
};
 
int main()
{
    try
    {
        T t[10];
    }
    catch(int)
    {
        cout << "catch";
    }
    return 0;
}

на этом коде MSVC2008 выдал ~T()~T() и крашнулся.
а gcc-4.3.4 выдал ~T()catch и нормально завершился.
надо проверить ISO: это баг MSVC или просто UB
Re[4]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 28.09.12 13:20
Оценка:
Здравствуйте, UniqueRSDN, Вы писали:

[...]
URS>вопрос в том, что будет проще. пока что особого профита я не увидел и предпочту вариант, в котором деструкторы
URS>все-таки не кидают ничего. эта дорожка хотябы протоптана.

всё правильно написано
А что насчёт объектов, которые создаются и живут только в scope кода, то есть не члены, не базы, не хранятся в контейнерах?
Допустим создаём std::ofstream, и сразу рядом с ним scope action на success (выполняет заданное действие в деструкторе, если не stack unwinding), в котором взводим exceptions и дёргаем close (который соответственно может кинуть).
Либо создаём свою небольшую обвёртку, которую по соглашению можно использовать только в scope кода.
Re: Legalize throwing destructors! D's scope(failure) and scope(success) in C++
От: Vamp Россия  
Дата: 01.10.12 17:14
Оценка:
Мое бескомпромиссное имхо — кидать из деструкторов пытаются те, кто не умеет их готовить. Деструктор — это то, что вызывается, когда объект разрушается, так? А что значит исключение? Это значит, что функция не смогла выполнить пост-условие, то есть, объект не может быть разрушен ни при каких обстоятельствах. Но это же абсурд! Что значит — не смог быть разрушен? И что теперь с ним делать? Как программе обходиться с объектом, который не может (а должен) быть разрушен, а? С точки зрения бизнес-логики и здравого смысла? Представь себе, что delete вдруг начал бы кидать исключения. Что это вообще может означать?
Нет, кидать из деструкторов — абсурд. Если какая-то операция принципиально может обломиться, ей не место в деструкторе.
Да здравствует мыло душистое и веревка пушистая.
Re[2]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 01.10.12 17:41
Оценка:
Здравствуйте, Vamp, Вы писали:

[...]
V>Нет, кидать из деструкторов — абсурд. Если какая-то операция принципиально может обломиться, ей не место в деструкторе.

Помимо непосредственно освобождения ресурсов(которое по своей сути не фэйлится, за очень редким исключением), на деструкторы навешиваются разного рода отложенные действия, которые могут фэйлится.
Яркий пример это flush у класса типа "File".

Один из углов под которыми можно рассматривать кидающие деструкторы:

1. Освобождение ресурсов не должно фейлится (“letting go of a resource” must never fail Herb Sutter http://cpp-next.com/archive/2012/08/evil-or-just-misunderstood/)
2. Освобождение ресурсов должно произойти в любом случае — утечек быть не должно
3. Поддержка отложенных действий (flush) — желательна. Такие действия могут фэйлится
4. Если отложенное действие фэйлится — пользователь должен узнать об этом. Проглатывание исключений (потеря информации) — не является хорошим решением.

Один из подходов который удовлетворяет данным требованиям (но не полагающийся на одновременное распространение нескольких исключений (которое естественно не поддерживается) ), состоит в том, что отложенные действия вызываются только в том случае, когда деструктор вызван не из-за раскрутки стэка.
Это приводит к двух-фазной деструкции:
class RAII_Deffered
{
    bool fail_on_flush;
public:
    // Normal contrustor
    RAII_Deffered(bool fail_on_flush_) : fail_on_flush(fail_on_flush_)
    {
        cout << "acquiring resource" << endl;
    }
    // Release part of destructor.
    // Herb Sutter: "letting go of a resource" must never fail
    // (http://cpp-next.com/archive/2012/08/evil-or-just-misunderstood/)
    TWO_STAGE_DESTRUCTOR_RELEASE(RAII_Deffered)
    {
        cout << "release resource" << endl;
    }
    // Deferred part of destructor. May fail(for instance fflush).
    // Called when object is destroyed due to normal flow, not stack unwinding
    TWO_STAGE_DESTRUCTOR_DEFERRED(RAII_Deffered)
    {
        cout << "flush pending actions on resource" << endl;
        if(fail_on_flush) throw 1;
    }
};

которая имеет следующую семантику:
~Foo(bool due_to_unwinding)
{
    try
    {
        if(!due_to_unwinding) deferred_part_of_destructor();
    }
    catch(...)
    {
        release_part_of_destructor();
        throw;
    }
    release_part_of_destructor();
}

И пример использования.
ИМХО — такой подход имеет смысл. Не вызывание отложенных действий во время раскрутки, делает их похожими на обычные действия которые следуют за throw — обычные действия игнорятся (естественно) во время раскрутки.

V>Мое бескомпромиссное имхо — кидать из деструкторов пытаются те, кто не умеет их готовить.


Как будешь готовить класс "File"? Где будет flush?
1. Во время каждой операции?
2. В деструкторе? как сообщать об ошибке?
3. В отдельной функции close/flush? Обязан ли пользователь её всегда вызывать? Вызывает ли её деструктор?
Re[3]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Vamp Россия  
Дата: 01.10.12 17:56
Оценка: +1 -1
EP>Помимо непосредственно освобождения ресурсов(которое по своей сути не фэйлится, за очень редким исключением), на деструкторы навешиваются разного рода отложенные действия, которые могут фэйлится.
EP>Яркий пример это flush у класса типа "File".
Флаш не должен фейлиться, потому, что в нормальных условиях не фейлиться fflush. У него, конечно, есть набор кодов ошибок, но все они должны проверяться ДО вызова деструктора. Среди всех возможных ошибок fflush нет ни одной, которую нельзя было бы проверить заранее — при последней записи, например.
Еще раз — все операции, которые могут завершиться неуспешно, выполняются ДО вызова деструктора.

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

Не понимаю, что тут можно сделать. Возьмем случай кидающего деструктора.


class A {... ~A() { ... throw ... } };
void foo() {
    A a;
}

void bar() {
   try {
     foo();
   } catch(...) {
       // И ЧТО?
   }
}


И что в catch? Объект a — он удален или где? Что вообще тут делать? Написать "случилась фигня"? Ну в этом случае можно безо всяких исключений terminate позвать прямо из деструктора. Дешево и сердито.
EP>Как будешь готовить класс "File"? Где будет flush?
EP>1. Во время каждой операции?
В нигде. Будет как отдельный метод. Из деструктора звать fflush не буду, только fclose.

EP>2. В деструкторе? как сообщать об ошибке?

Никак. Fflush реально не возвращает ошибок.

EP>3. В отдельной функции close/flush? Обязан ли пользователь её всегда вызывать? Вызывает ли её деструктор?

См. выше.
Да здравствует мыло душистое и веревка пушистая.
Re[4]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: SleepyDrago Украина  
Дата: 01.10.12 18:47
Оценка:
Здравствуйте, Vamp, Вы писали:

...
EP>>Как будешь готовить класс "File"? Где будет flush?
EP>>1. Во время каждой операции?
V>В нигде. Будет как отдельный метод. Из деструктора звать fflush не буду, только fclose.

просвещайся.
http://www.cplusplus.com/reference/clibrary/cstdio/fclose/
On failure, EOF is returned.

EP>>2. В деструкторе? как сообщать об ошибке?

V>Никак. Fflush реально не возвращает ошибок.
гениально. программы не совершают ошибок. эй юзер, воткни обратно флешку !

EP>>3. В отдельной функции close/flush? Обязан ли пользователь её всегда вызывать? Вызывает ли её деструктор?

V>См. выше.
удачи.
Re[5]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Vamp Россия  
Дата: 01.10.12 18:55
Оценка: +1
V>>В нигде. Будет как отдельный метод. Из деструктора звать fflush не буду, только fclose.
SD>просвещайся.
SD>http://www.cplusplus.com/reference/clibrary/cstdio/fclose/
SD>On failure, EOF is returned.
А почему ты думаешь, что я этого не знаю?? Возьми и посмотри, какие для того могут быть причины. Они все могут быть проверены заранее.

V>>Никак. Fflush реально не возвращает ошибок.

SD>гениально. программы не совершают ошибок. эй юзер, воткни обратно флешку !
Программы действительно не совершают ошибок. Ошибки совершают некоторые программисты — например, когда начинают швыряться исключениями из деструктора.

SD>удачи.

И тебе не грустить.
Да здравствует мыло душистое и веревка пушистая.
Re[4]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Evgeny.Panasyuk Россия  
Дата: 01.10.12 19:13
Оценка:
Здравствуйте, Vamp, Вы писали:

V>Среди всех возможных ошибок fflush нет ни одной, которую нельзя было бы проверить заранее — при последней записи, например.


Откуда такая гарантия, учитывая что запись буферизованная?

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

V>Не понимаю, что тут можно сделать. Возьмем случай кидающего деструктора.
[...]
V>И что в catch? Объект a — он удален или где? Что вообще тут делать? Написать "случилась фигня"?

То же самое, что и при обычных исключениях
Их обработка обычно происходит несколькими уровнями выше, чем непосредственно код в котором они произошли, и все объекты тоже уже удаленны. Хотя бы потому, что во многих случаях в том месте где они произошли — недостаточно информации для их обработки/исправления. Это одна из главных фич исключений.
Использовать исключения, для кода, в котором каждый вызов функции обвёрнут в try/catch — глупо, уж лучше коды возврата (или что ты хотел показать свои примером?).

V>Ну в этом случае можно безо всяких исключений terminate позвать прямо из деструктора. Дешево и сердито.


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

EP>>Как будешь готовить класс "File"? Где будет flush?

EP>>1. Во время каждой операции?
V>В нигде. Будет как отдельный метод. Из деструктора звать fflush не буду, только fclose.
EP>>2. В деструкторе? как сообщать об ошибке?
V>Никак. Fflush реально не возвращает ошибок.
EP>>3. В отдельной функции close/flush? Обязан ли пользователь её всегда вызывать? Вызывает ли её деструктор?
V>См. выше.

Ну вот абстрактный пример, два файла пишутся параллельно:
void fsome(/* ... */)
{
    OutFile a("a"),b("b");
    a.write("Hello");
    b.write("World");
    // ...
    b.flush(); // Fail #1
    a.flush(); // Fail #2
}

У fsome, следующие постусловие — данные записаны в оба файла. Если fsome не может достичь своего постусловия — то кидается исключение.
Что ты предлагаешь делать в местах "Fail #1" и "Fail #2"?
Моё вариант:
void fsome(/* ... */)
{
    OutFile a("a"),b("b");
    a.write("Hello");
    b.write("World");
    // ...
    // b.~OutFile():
    //     1. flush - Fail #1, throw exception
    //     2. fclose - called anyway
    // a.~OutFile() - called due to stack unwinding:
    //     1. flush is not called, due to stack unwinding
    //     2. fclose - called anyway
}
Re[5]: Legalize throwing destructors! D's scope(failure) and scope(success) in C
От: Vamp Россия  
Дата: 01.10.12 19:27
Оценка: +1
EP>Откуда такая гарантия, учитывая что запись буферизованная?
Ну укажи, какие именно ошибки из возвращаемых fflush могут возникнуть внезапно.

EP>То же самое, что и при обычных исключениях

EP>Их обработка обычно происходит несколькими уровнями выше, чем непосредственно код в котором они произошли, и все объекты тоже уже удаленны. Хотя бы потому, что во многих случаях в том месте где они произошли — недостаточно информации для их обработки/исправления. Это одна из главных фич исключений.
При обычных исключениях объект не создан, и никакой работы еще не проделано. То есть если файл нельзя создать — то мы кидаем исключение (хотя я бы и в этом случае обошелся кодом возврата) — и выдаем окошечко, файл не создан, выбери другой.
А когда у тебя файл не закрыт — что ты будешь делать? То есть, ты писал-писал (особенно хорошо, если данные приходили по сети) — и вдруг опаньки? Что РАЗУМНОГО ты можешь сделать в этой ситуации?

EP>После обычных исключений, ты тоже стреляешь программе в голову?

Нет. Но см. выше.

EP>>>Как будешь готовить класс "File"? Где будет flush?


EP>Ну вот абстрактный пример, два файла пишутся параллельно:

EP>
EP>void fsome(/* ... */)
EP>{
EP>    OutFile a("a"),b("b");
EP>    a.write("Hello");
EP>    b.write("World");
EP>    // ...
EP>    b.flush(); // Fail #1
EP>    a.flush(); // Fail #2
EP>}
EP>

EP>У fsome, следующие постусловие — данные записаны в оба файла. Если fsome не может достичь своего постусловия — то кидается исключение.
Оставь ты flush в покое. Как думаешь, почему у fstream деструктор не швыряет, а?
Да здравствует мыло душистое и веревка пушистая.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.