【问题标题】:Legacy code throws exception in destructor旧代码在析构函数中抛出异常
【发布时间】:2019-06-07 20:07:10
【问题描述】:

我正在维护相当旧的遗留代码。我的先驱者是异常处理领域的先驱(

他们实现了一种很好的投掷方式:

TL_THROW_EXCEPTION(ISQL_MSG_XML_PARSER_ERROR) << msg; 

TL_THROW_EXCEPTION 扩展为:

TLThrowTec::CTLThrowExceptionTechnical::ThrowT(__FILE__,__LINE__, 
                                      ISQL_MSG_XML_PARSER_ERROR) << msg;

它在堆栈上创建一个 TLThrowTec::CTLThrowExceptionTechnical 实例,并使用移位操作来设置消息字符串。析构函数创建并抛出异常。

stackoverflow 中的一个 2008 年条目解释了那一年的状态: throwing exceptions out of a destructor 那时它似乎已经奏效了。

但现在 Visual Studio 17 允许投掷,但不再允许捕捉。

由于遗留代码是整个系统的一部分,它会生成日志文件条目,例如“未处理的操作系统异常”。

我想在不对源代码进行太多更改的情况下恢复捕获逻辑。最好为 TL_THROW_EXCEPTION 提供#define。

有没有办法重新定义宏,以便在消息中抛出异常?

如果我搜索 TL_THROW_EXCEPTION 这是最后一行:

匹配行:770 匹配文件:217 搜索的总文件:3159

我不喜欢触摸所有这些。

【问题讨论】:

  • VS2017 运行时现在强制执行异常规范,就像它应该遵循语言规范一样。您只需要正确定义析构函数即可恢复遗留行为。所以,大致是~CFoo() throw() { .... }。至少在他们强制执行 C++20 之前你会很好,那可能需要一段时间。
  • 你能修改ThrowT吗?

标签: c++ visual-c++


【解决方案1】:

现代 C++ 默认将所有析构函数指定为不抛出。如果任何指定为非抛出的函数抛出异常,程序将立即终止。这是因为抛出析构函数是不受欢迎的。在堆栈展开期间自动调用析构函数,而异常已经在传输中。在这种情况下,再次抛出本身将终止程序。如果包含对象的析构函数抛出,所有标准容器都会警告 UB。

但是您的用例不容易触发任何这些。 ThrowT 在我看来,对象似乎是要被创建为立即抛出某些东西的临时对象,它们不会在其他东西的堆栈展开期间徘徊以调用其析构函数。因此,我们可以将它们标记为再次投掷以恢复功能。像这样的……

ThrowT::~ThrowT() noexcept(false) {
  // as before
}

... 可以让宏再次工作。

【讨论】:

    【解决方案2】:

    只需将异常生成类型调整为成为异常,然后使用:

    #undef TL_THROW_EXCEPTION
    
    #define TL_THROW_EXCEPTION(msg_) \
        throw YourExceptionType(__FILE__, __LINE__, (msg_))
    

    由于throw 的优先级低于&lt;&lt;,这将首先计算聚合表达式,然后抛出其结果。

    【讨论】:

      【解决方案3】:

      只需修复该类:

      1. std::uncaught_exceptions()(复数,单数)的结果保存在ctor中。
      2. 标记 dtor noexcept(false) 以启用传播异常。
      3. 只有在调用std::uncaught_exceptions() 时才抛出 dtor,返回保存的计数。否则,尝试构建异常会导致异常,并且将您自己的异常扔进混乱中会得到std::terminate 的调用。

      我希望你的构造函数使用std::stringstream。否则,也请考虑修复该疏忽。

      【讨论】:

        【解决方案4】:

        有没有办法重新定义宏?

        是的,只需取消定义,然后重新定义,例如:

        #undef  TL_THROW_EXCEPTION 
        
        #define TL_THROW_EXCEPTION ...
        

        当然,如果您能想办法将其设为class 或函数,则最好在#undef 之后执行此操作,而不是使用宏。

        更像是:

        #undef  TL_THROW_EXCEPTION 
        
        class TL_THROW_EXCEPTION 
        { ... }
        

        然后定义构造函数来创建一个实例,为您提供所需的行为。

        【讨论】:

          猜你喜欢
          • 2015-08-26
          • 2022-01-16
          • 2016-12-17
          • 2013-04-03
          • 1970-01-01
          • 2012-04-15
          • 2012-10-28
          相关资源
          最近更新 更多