У меня вообще вопрос пошёл из следующей ситуации (как раз описанной ivank в предыдущем посте): Есть форма, нарисованная на билдере, кнопной, с таймером и запросом. Автоматика такая:
1. Нажали кнопку, она залипла
2. через одну секнду выключилась (disable), запустился запрос
3. запрос отработал, выдал данные, включил кнопка тлипла обратно и включилась
теперь в запросе происходит исключение, кнопка не возвращается из своего необычного состояния - потому что нет откатов
Вот если бы это всё выполнялось "одной функцией", то есть без разбивки на OnClick и OnTimer, выглядело бы так:
Код:

struct restore_pushed_t
{
TButton* button;
restore_pushed_t(TButton* b) : button(b)
{
SendMessage(button->Handle, BM_SETSTATE, BS_PUSHED, 0);
}
~restore_pushed_t()
{
SendMessage(button->Handle, BM_SETSTATE, 0, 0);
}
struct restore_enabled_t
{
TButton* button;
restore_enabled_t(TButton* b) : button(b)
{
button->Enabled = false;
}
~restore_enabled_t()
{
button->Enabled = true;
}
}
void Form1::OnClick(TObject* /*sender*/)
{
restore_pushed_t restore_pushed(Button1);
sleep(1000);
restore_enabled_t restore_enabled(Button1);
...
}
Теперь что ни случись, кнопка вернётся в своё нормальное состояние, а исключение не убьёт программу, потому что оно отловится снаружи Form1::OnClick борландовской библиотекой. Если что-то вернуться не сможет (в одном из деструкторов вспомогательных классов выплывет исключение), будет автоматически вызвана функция unhandled(), которая вызовет abort().
1. Чтобы на время выполнения обработки не заблокировать процесс, вполне логично запихнуть всё это в отдельный TThread. Вроде бы теперь логично обрамлять основной цикл TThread скобками try-catch. А как вести себя, если 2 нити взаимосвязаны и сдой в обной должен разрушить другую?
2. Вариант с использованием событийной системы, чтобы не плодить нити. Разбиваем void Form1::OnClick(TObject* /*sender*/) на части:
Код:

struct restore_obj
{
restore_obj() : fprev(gstack_top)
{
gstack_top = this;
}
virtual ~restore_obj()
{
gstack_top = fprev;
}
static void unwind() throw()
{
while (gstack_top) delete gstack_top;
}
private:
restore_obj* fprev;
static restore_obj* gstack_top;
};
struct restore_pushed_t : restore_obj {...}
struct restore_enabled_t : restore_obj {...}
void Form1::OnClick(TObject* /*sender*/)
{
try {
// включает таймер в конструкторе
// выключает в деструкторе
new restore_pushed_t(Button1, Timer1);
}
catch(...)
{
// log errors
restore_obj::unwind();
throw;
}
}
void Form1::OnTimer(TObject* /*sender*/)
{
try {
Timer1->Enabled = false;
new restore_enabled_t(Button1);
...
restore_obj::unwind(); // вернуть в рабочем режиме
}
catch(...)
{
// log errors
restore_obj::unwind();
throw;
}
}
чтобы не писать каждый раз try..catch можно поместить restore_obj::unwind(); в OnException. Вроде теперь всё приятно работает. Но опять возникаеи вопрос: а если я сделаю несколько стеков? в одном произошло исключение, что делать со вторым? как логичнее быть?