Код очистки в деструкторе исключения C++

Можем ли мы использовать деструктор исключения как место для размещения некоторого кода очистки?

Таким образом, мы можем позволить клиенту контролировать этап завершения в отличие от RAII. Это хороший или плохой дизайн? Это правильное решение в контексте ООП и C++?

В настоящее время я работаю над асинхронной процедурой, которая сама запускает несколько асинхронных задач. Шаблон выглядит следующим образом:

struct IAsyncResult
{
    ...
    virtual void EndCall() const;
}
typedef std::shared_ptr<IAsyncResult> IAsyncResultPtr;

struct IAsyncTask
{
    virtual IAsyncResultPtr BeginTask() = 0;
    virtual void EndTask(IAsyncResultPtr async) const = 0;
}

class CompositeTask : public IAsyncTask
{
    …
}

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

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

Таким образом, мое текущее решение включает в себя пользовательское исключение, которое выдается из метода BeginAsync CompositeTask в случае, если не удалось запустить одну задачу. Это позволяет клиенту контролировать этап очистки:

class composite_async_exception : public std::exception
{
    std::vector<IAsyncResultPtr> successfully_started_tasks;
    mutable bool manage_cleanup;
public:
    composite_async_exception(std::vector<IAsyncResultPtr> const& _successfully_started_tasks)
        : successfully_started_tasks(_successfully_started_tasks)
        , manage_cleanup(true)
    {
    }

    virtual ~composite_async_exception() throw()
    {
        if(!manage_cleanup)
            return;
        for( auto task = successfully_started_tasks.begin(); task != successfully_started_tasks.end(); ++task)
        {
            task->CancelTask();
        }
    }

    void Giveup() const throw()
    {
        manage_cleanup = false;
    }
};

И клиент использует код как показано:

try
{
    compositeTask.BeginAsync();
}
catch(composite_async_exception const& ex)
{
    //prevent the exception to cancel tasks
    ex.Giveup();
    // some handling
}

Существуют ли лучшие практики для решения такой ситуации?

1 ответ

Решение
  • Исключение может быть скопировано, деструктор будет вызываться несколько раз. В вашем случае это не проблема.
  • Механизм обработки исключений может остановить ваши задачи, уничтожив объект временного исключения, прерывающий ваши задачи в точке выброса, а не при обработке.

Чтобы убедиться в этом, нужно прочитать стандарт, что мне лень делать.

Другие вопросы по тегам