【问题标题】:Static analysis of noexcept "violations" in C++C++中noexcept“违规”的静态分析
【发布时间】:2021-11-04 03:45:48
【问题描述】:

我正在尝试编写异常安全代码。我发现使用 C++11 的 noexcept 说明符使这个目标更容易实现。

当然,一般的想法是,当且仅当它调用的所有函数也都标记为“noexcept”时,一个函数才应该标记为“noexcept”。

问题在于,在大型代码库中,来自不同人的补丁经常合并在一起,很难确保保持这种一致性。

所以我希望能够运行静态分析,该分析可以列出标记为“nothrow”的函数调用未标记为“nothrow”的函数的所有位置。

据我在手册页中看到的,GCC 无法帮助我。有没有可以帮助我的独立工具?或者其他一些编译器?

【问题讨论】:

  • 这总是一个好主意,但在实践中它可能会失败,因为你的无抛出函数的复杂性增加了。我在别处看到的一个例子:double safe_sqrt(double x) { if (x < 0) throw "no"; return sqrt(x); } double abs_sqrt(double x) noexcept { return safe_sqrt(abs(x)); } noexcept 函数调用了一个抛出函数,但这是安全的,因为无法到达抛出路径。但是,静态分析不再像“我是否调用 noexcept 函数”那么简单。当然,仍然可行,但要困难得多。还有一些解决方法,但我怀疑它们在每种情况下都很简单(这个是)。
  • 是的,这是个问题,但是,在这种情况下,您要么接受分析仪的“误报”,要么干脆不将 abs_sqrt 标记为“noexcept”。我仍然觉得这样的工具会提供很大的价值。
  • 顺便说一句,你可能想读这个:akrzemi1.wordpress.com/2011/06/10/using-noexcept(有人把它贴到我的 noexcept Q 上)
  • 看看clang...它可能还没有实现,但是扩展分析器很简单
  • @NoSenseEtAl:根据 andrzej 的博客“不投掷保证的静态检查,以及用于本地禁用检查的改进工具。”很可能成为下一个 C++ 版本的一部分。不错!

标签: c++ exception static-analysis noexcept


【解决方案1】:

如果您通过使用程序的 ABT 来避免指向函数的指针(并认为它们不安全),这当然是可能的。 Clang 的 AST 是(尽管它的名字)这样的 ABT:您将看到函数的声明和定义。通过一次完成一个定义的工作,您已经有了一个良好的基准。

另一方面,我想知道这是否实用。看,问题是任何执行内存分配的函数都(自愿地)被标记为可能抛出(因为new从不返回null,而是抛出bad_alloc)。因此,在大多数情况下,您的 noexcept 将仅限于少数功能。

当然还有像@GManNickG这样的动态条件暴露,例如:

void foo(boost::optional<T> const t&) {
    if (not t) { return; }

    t->bar();
}

即使T::barnoexcept,取消引用optional&lt;T&gt; 也可能会抛出(如果什么都没有)。当然,这忽略了我们已经排除了这个事实(这里)。

如果没有明确的条件,函数何时可能throwstatic 分析可能会证明...无用。语言习语的设计考虑到了例外情况。


注意:作为题外话,可以重写可选类以免暴露解引用,因此是noexcept(如果回调是):

template <typename T>
class maybe {
public:

    template <typename OnNone, typename OnJust>
    void act(OnNone&& n, OnJust&& j) noexcept(noexcept(n()) and 
                                              noexcept(j(std::declval<T&>())))
    {
        if (not _assigned) { n(); return; }
        j(*reinterpret_cast<T*>(&_storage));
    }

private:
    std::aligned_storage<sizeof(T), alignof(T)>::type _storage;
    bool _assigned;
};

// No idea if this way of expressing the noexcept dependency is actually correct.

【讨论】:

  • 好吧,动态分配可能会失败,这只是生活中的事实。出于这个原因(和其他原因),我努力避免动态分配。在我处理的代码中,动态分配是所有函数都不是“nothrow”的主要原因。
  • @KristianSpangsege:不幸的是,它可能会失败,但至少在 Linux 上它不会因为过度使用,而是会出现段错误。因此,您为此支付了noexcept 的价格,但您的程序将在抛出bad_alloc 之前崩溃:这是一个失败/失败的情况。
  • 然而,在 OSX 上,没有办法限制进程的虚拟内存,所以在这个平台上,动态分配确实不可能失败。
  • @Mattieu - 忘记循环。我的观点只是,在给定任何输入的情况下,单个程序在理论上不可能决定任意程序是否会抛出。 EOT
  • @JiveDadson:当然,但我们不是在谈论任意程序;这里没有动态条件处理!我们并不是要求编译器证明没有一个函数被 throw 调用,而是要证明所有被调用的函数都被 @​​987654334@ 注释。这要简单得多!当然,它也更具限制性,因为即使您尝试动态断言在您的情况下它不会抛出 =>,您也不能调用潜在的抛出函数,但我认为这种简化可以允许证明。
【解决方案2】:

clang-tidy 检查 bugprone-exception-escape 尝试查找可能直接或间接抛出异常的函数,而这些函数不应该抛出异常。这包括检查标有throw()noexcept 的函数。

https://clang.llvm.org/extra/clang-tidy/checks/bugprone-exception-escape.html

【讨论】:

    猜你喜欢
    • 2019-03-24
    • 2020-09-26
    • 2012-08-01
    • 1970-01-01
    • 2014-05-30
    • 2012-07-17
    • 2013-09-20
    • 2012-06-23
    • 1970-01-01
    相关资源
    最近更新 更多