如何在不同的上下文(例如线程)中保留原始异常类型信息

How to preserve original exception type info accross different contexts (e.g. threads)?

本文关键字:保留 原始 信息 类型 异常 线程 上下文      更新时间:2023-10-16

作为我关于从std::rethrow_exception(e);捕获正确异常问题的后续问题,其中e是std::exception_ptr,我想知道如何将原始异常信息保留在当前异常块之外?

在库中,我在不同的地方保存exception_ptr实例,因此(理论上)应该可以获得原始异常及其包含的所有信息,例如错误报告函数中的信息。异常指针是在catch子句中为一个异常基类创建的,因此std::make_exception_ptr只创建基类型异常的副本,而不是原始异常(这就是我在链接问题中看到的问题)。

存储引用当然是个坏主意(实际的异常对象早就不见了)。存储异常的副本(通过基于类型)会给我带来同样的对象切片问题,就像使用std::exception_ptr时一样。那么,我还有什么其他选择呢?

Kerrek SB的第一条评论让我想到了如何解决这个问题。我现在有了一个函数,可以重新抛出异常,并使用已知的子类从中创建单独的异常指针,而不是从捕获的基类型创建exception_ptr实例,我可以保留这些指针并获得所需的信息:
std::exception_ptr DefaultErrorStrategy::reportError(Parser *recognizer) {
  // If we've already reported an error and have not matched a token
  // yet successfully, don't report any errors.
  if (inErrorRecoveryMode(recognizer)) {
    return nullptr; // don't report spurious errors
  }
  beginErrorCondition(recognizer);
  std::exception_ptr result = nullptr;
  try {
    // We are called from catch() clauses, so we can just rethrow the current exception and catch
    // the original exception type, so we don't lose information (caused by object slicing).
    throw;
  } catch (NoViableAltException &ne) {
    result = std::make_exception_ptr(ne);
    reportNoViableAlternative(recognizer, ne);
  } catch (InputMismatchException &ne) {
    result = std::make_exception_ptr(ne);
    reportInputMismatch(recognizer, ne);
  } catch (FailedPredicateException &ne) {
    result = std::make_exception_ptr(ne);
    reportFailedPredicate(recognizer, ne);
  } catch (RecognitionException &ne) {
    result = std::make_exception_ptr(ne);
    recognizer->notifyErrorListeners(ne.getOffendingToken(), ne.what(), result);
  }
  return result;
}

这个函数的调用方式如下:

    try {
      visitState(p);
    }
    catch (RecognitionException &e) {
      setState(_atn.ruleToStopState[p->ruleIndex]->stateNumber);
      getContext()->exception = getErrorHandler()->reportError(this);
      recover(e);
    }

更新:

我在错误的假设下创建了该代码(current_exception和make_exception_ptr做了相同的操作->创建副本)。事实并非如此,这个答案中的代码确实是错误的,我保留它只是为了参考,如何而不是这样做。