Как можно сохранить исключение?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Сохранение исключения для последующей обработки
Сохранение исключения — это процесс захвата информации об исключении в блоке catch, чтобы использовать эту информацию позже, вне области, где исключение было выброшено.
Основной механизм: std::exception_ptr
std::exception_ptr — это умный указатель на исключение, который позволяет сохранить исключение и передать его в другой контекст.
#include <exception>
std::exception_ptr saved_exception = nullptr;
try {
throw std::runtime_error("Something went wrong");
} catch (...) {
saved_exception = std::current_exception(); // Сохраняем
}
if (saved_exception) {
try {
std::rethrow_exception(saved_exception);
} catch (const std::runtime_error& e) {
std::cout << "Caught: " << e.what() << std::endl;
}
}
Практический пример: Асинхронные операции
Сценарий: Запущена асинхронная операция в отдельном потоке, которая может выбросить исключение. Нужно обработать его в основном потоке.
class AsyncTask {
std::exception_ptr error_state;
std::thread worker;
public:
AsyncTask() {
worker = std::thread([this]() {
try {
do_heavy_work(); // Может выбросить
} catch (...) {
error_state = std::current_exception();
}
});
}
void wait_for_completion() {
worker.join();
if (error_state) {
try {
std::rethrow_exception(error_state);
} catch (const std::exception& e) {
std::cerr << "Task failed: " << e.what() << std::endl;
}
}
}
};
Сохранение с контекстом
struct ErrorSnapshot {
std::string error_message;
std::exception_ptr exception;
std::string context_info;
int error_code;
};
ErrorSnapshot snapshot;
try {
process_data(huge_dataset);
} catch (const std::exception& e) {
snapshot.error_message = e.what();
snapshot.exception = std::current_exception();
snapshot.context_info = "Processing record #" + std::to_string(current_id);
snapshot.error_code = -1;
}
// Позже, например в логе
if (snapshot.exception) {
try {
std::rethrow_exception(snapshot.exception);
} catch (const std::exception& e) {
log_to_file(snapshot.context_info + ": " + e.what());
}
}
Очередь исключений
template<typename T>
class SafeExceptionQueue {
std::queue<std::pair<std::exception_ptr, std::string>> queue;
std::mutex mtx;
public:
void capture_exception(const std::string& context) {
std::lock_guard<std::mutex> lock(mtx);
queue.push({std::current_exception(), context});
}
void process_all() {
std::lock_guard<std::mutex> lock(mtx);
while (!queue.empty()) {
auto [exc, ctx] = queue.front();
queue.pop();
try {
std::rethrow_exception(exc);
} catch (const std::exception& e) {
std::cerr << "Context: " << ctx << " => " << e.what() << std::endl;
}
}
}
};
Result паттерн (функциональный подход)
template<typename T>
class Result {
std::optional<T> value;
std::exception_ptr error;
public:
static Result success(const T& v) {
Result r;
r.value = v;
return r;
}
static Result failure() {
Result r;
r.error = std::current_exception();
return r;
}
bool is_ok() const { return value.has_value(); }
bool is_error() const { return error != nullptr; }
T get_value() const { return value.value(); }
void rethrow_if_error() const {
if (error) std::rethrow_exception(error);
}
};
Result<int> safe_parse(const std::string& str) {
try {
return Result<int>::success(std::stoi(str));
} catch (...) {
return Result<int>::failure();
}
}
Result<int> res = safe_parse("not a number");
if (res.is_error()) {
res.rethrow_if_error();
}
RAII с сохранением ошибки
class DatabaseTransaction {
Database& db;
std::exception_ptr transaction_error;
bool committed = false;
public:
DatabaseTransaction(Database& d) : db(d) { db.begin(); }
void execute(const std::string& sql) {
try {
db.execute(sql);
} catch (...) {
transaction_error = std::current_exception();
}
}
void commit() {
if (transaction_error) {
db.rollback();
std::rethrow_exception(transaction_error);
}
db.commit();
committed = true;
}
~DatabaseTransaction() {
if (!committed) {
try {
db.rollback();
} catch (...) {}
}
}
};
Сохранение с метаданными
struct CapturedError {
std::exception_ptr exception;
std::string what_message;
std::string type_name;
std::chrono::system_clock::time_point timestamp;
std::string source_file;
int source_line;
};
#define SAVE_EXCEPTION(container, file, line) \
try {} catch (...) { \
CapturedError err; \
err.exception = std::current_exception(); \
try { std::rethrow_exception(err.exception); } \
catch (const std::exception& e) { err.what_message = e.what(); } \
catch (...) { err.what_message = "Unknown exception"; } \
err.timestamp = std::chrono::system_clock::now(); \
err.source_file = file; \
err.source_line = line; \
container.push_back(err); \
}
std::vector<CapturedError> errors;
try {
dangerous_operation();
} catch (...) {
SAVE_EXCEPTION(errors, __FILE__, __LINE__);
}
Преимущества сохранения
1. Отложенная обработка — обработаем ошибку когда удобно 2. Логирование — сохраним в БД или файл 3. Multithread — передадим ошибку из рабочего потока 4. Транзакции — откатим и затем обработаем 5. Агрегирование — соберём несколько ошибок
Ограничения
- exception_ptr работает только в catch блоке
- Нельзя выбрасывать из деструктора
- Overhead памяти при сохранении большого количества
Резюме
Сохранение исключения через std::exception_ptr позволяет:
- Обработать ошибку позже
- Передать ошибку между потоками
- Логировать с полным контекстом
- Реализовать функциональные паттерны (Result/Optional)
- Гарантировать очистку ресурсов (RAII)