Использование uncaught_exception для обработки ошибок

У меня следующая проблема.

У меня есть соединение с базой данных, которое перерабатывается (возвращается в пул).

Например:

{
 session sql(conn_str); // take connection from pool

 sql.exec("insert into ...")
} // at the end of the scope return connection to pool

Однако в некоторых случаях рециркуляция может быть ошибочной — например, отключение или какая-либо другая существенная ошибка.

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

session::exec(...)
{
   guard g(this)

   real_exec(...);
}

Где охранять:

class guard {
public:
   guard(session *self) : self_(self) {}
   ~guard() {
      if(std::uncaught_exception()) {
        self->mark_as_connection_that_should_not_go_to_pool();
      }
   }
}

Теперь мне известно о http://www.gotw.ca/gotw/047.htm, который не рекомендует использовать std::uncaught_exception в другом случае, я также не вижу ничего плохого в моем коде, приведенные примеры обсуждаются.

Возможны ли проблемы с этим кодом.

Примечание.

  1. Я хочу, чтобы это изменение было ненавязчивым, чтобы бэкенд SQL мог выбрасывать, а не проверять для каждого случая, критично оно или нет.
  2. Я не хочу, чтобы пользователь предпринимал какие-либо действия по этому поводу, чтобы для него это было прозрачно.

person Artyom    schedule 07.12.2011    source источник


Ответы (1)


Я не вижу никаких преимуществ вашего метода перед чем-то более простым:

session::exec()
{
    try
    {
        real_exec();
    }
    catch(...)
    {
        mark_as_connection_that_should_not_go_to_pool();
        throw;
    }
}

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

#define GUARD try {
#define ENDGUARD } catch(...) { mark_as_connection_that_should_not_go_to_pool(); throw; }

session::exec()
{
    GUARD
    real_exec();
    ENDGUARD
}

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

session::exec()
{
    mark_as_connection_that_should_not_go_to_pool();
    real_exec();
    mark_as_connection_that_may_go_to_pool();
}

Наконец, чтобы ответить на вопрос, будет ли uncaught_exception работать так, как вы изложили, я приведу цитату из документации Microsoft по этой функции:

В частности, uncaught_exception вернет true при вызове из деструктора, который вызывается во время очистки исключения.

Похоже, он делает именно то, что вы ожидаете.

person Mark Ransom    schedule 07.12.2011
comment
Причина, по которой я не использую этот метод, заключается в том, что он гораздо более подробный. Так что если у вас есть два десятка функций, которые делают одно и то же, охранник - это более простой и понятный способ решения такой проблемы ИМХО. - person Artyom; 07.12.2011
comment
Также std::uncaught_exception() является стандартной функцией, возможно, не так часто используемой, как try/catch - person Artyom; 07.12.2011
comment
Есть ли какой-то конкретный сценарий, который делает использование std::uncaught_exception() проблематичным для этого конкретного случая? - person Artyom; 08.12.2011
comment
@Artyom, я ничего не знаю, но, как вы говорите, это редко используемая функция. Я добавил подтверждающее заявление к моему ответу. - person Mark Ransom; 08.12.2011