В общем, ситуация такая.
Я разрабатываю реализацию Trie
(префиксное дерево) и столкнулся с неприятной (пока только теоретической) проблемой.
Дерево, которое я разрабатываю, должно обеспечивать гарантию безопасности при исключениях: если с деревом не удалось что-то сделать, то структура остается в том же состоянии, что и до начала операции.
Например, если посреди процесса вставки выяснилось, что памяти больше нет (или возникла другая проблема), тогда происходит подтирание того, что не успело сконструироваться до конца. Все оборванные операции откатываются. Незаконченные ветки уничтожаются.
В какой-то момент я заметил, что в нескольких местах происходит дублирование кода. Конкретно кода, который поднимается вверх по дереву и уничтожает каждый узел ветки, у которого нет потомков, и список хранимых значений которого пуст.
Эта функциональность нужна при:
Ее я вынес в отдельный метод, и вдруг понял, что этот метод может генерировать исключения.
То есть, получилось что-то вроде:
void insert(...)
{
// ...
try
{
// ...
}
catch (...)
{
try
{
erase_branch(...);
}
catch (...)
{
throw;// ???
}
throw;
}
}
Повторяю, проблема пока что теоретическая, потому что erase_branch()
может сгенерировать исключение лишь в том случае, когда структура дерева нарушена, например из-за логической ошибки при реализации.
Я практически уверен, что переход от исключений к кодам возврата проблему не решит, потому что вместо падения программы мы получим структуру с некорректным состоянием.
Единственный вариант побороть это - добавить в структуру флаг о том, что структура находится в противоречивом состоянии.
Подскажите, верно ли мое понимание данной ситуации?
Может быть, помимо флага корректности существует другой способ, чтобы не обрушивать всю программу, но при этом сообщить, что структурой дальше пользоваться нельзя?
можно попробовать скомбинировать варанты
void insert(...)
{
// ...
try
{
// ...
}
catch (...)
{
try
{
erase_branch(...);
}
catch (...)
{
m_inconsistent = true;
throw std::runtime_error("Error inserting data.");
}
throw;
}
}
...
вызывающий код:
try
{
trie.insert(...);
}
catch(const std::exception& e)
{
std::cerr << e.what() << std::endl;
if (trie.is_consistent() == false)
{
// Обработка несогласованного состояния / завершение работы
}
}
Но вообще ваша логика верная, если возникло исключение при обработке исключения, его надо обработать. Эту обязанность можно возложить на вызывающую сторону (тогда ваш вложенный блок трай-кэтч переедет в вызывающий код) либо делать самому
P.S. решение 2 - это сделать обработку несогласованного состояния в вашем методе insert, но после обработки выбросить оттуда специфичную ошибку (пронаследованную от std::exception). К сожалению в вопросе мало данных, чтобы дать более осмысленный ответ
std::terminate
будет, это прописано в стандарте. Именно поэтому не нужно допускать выброса исключений за пределы деструкторов.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Подскажите как скомпилировать файлИспользую g++ пытаюсь скомпилить