Clang 问题 -Wunused-value 取决于是否从宏调用代码

Clang issues -Wunused-value depending on whether the code is called from a macro

我使用了一个名为 CHECK 的特殊断言宏。它是这样实现的:

#define CHECK(condition)  check(condition).ok ? std::cerr : std::cerr

用户可以选择提供断言失败时打印的附加信息:

CHECK(a.ok());
CHECK(a.ok()) << a.to_string();

注意宏定义中的三元运算符。它确保 a.to_string() 仅在断言失败时执行。到目前为止,一切都很好。我已经使用这个(和其他类似的)宏很长时间了,没有任何问题。

但最近我发现clang发出关于第二个std::cerr的“表达式结果未使用[-Wunused-value]”警告如果CHECK在另一个宏中使用:

#define DO(code)  do { code } while(0)

int main() {
    do { CHECK(2 * 2 == 4); } while(0);  // no warning
    DO( CHECK(2 * 2 == 4); );  // warning
}

完整示例:https://godbolt.org/z/5bfnEGqsn.

这对我来说毫无意义。为什么此诊断取决于代码是否从宏扩展而来? GCC 在任何一种情况下都不会发出警告。

两个问题:

我认为这不是我在这里建议的好的解决方案,但您可以更改代码,以便始终使用 std::cerr,方法是将 check(condition).ok ? std::cerr : std::cerr 更改为 check(condition).ok ? std::cerr << "" : std::cerr << "":

#include <iostream>


struct CheckResult {
    CheckResult(bool ok_arg) : ok(ok_arg) {}
    ~CheckResult() { if (!ok) abort(); }
    bool ok;
};

inline CheckResult check(bool ok) {
    if (!ok) std::cerr << "Assertion failed!\n";
    return CheckResult(ok);
}

#define CHECK(condition)  \
    check(condition).ok ? std::cerr << "" : std::cerr << ""

#define DO(code)  \
    do { code } while(0)


int main() {
    do { CHECK(2 * 2 == 4); } while(0);
    DO( CHECK(2 * 2 == 4); );
}

您可以做的另一件事是使用 returns std::cerr:

的函数
#include <iostream>


struct CheckResult {
    CheckResult(bool ok_arg) : ok(ok_arg) {}
    ~CheckResult() { if (!ok) abort(); }
    bool ok;
};

inline CheckResult check(bool ok) {
    if (!ok) std::cerr << "Assertion failed!\n";
    return CheckResult(ok);
}

[[maybe_unused]] inline std::ostream & get_ostream() {
    return std::cerr;
}

#define CHECK(condition)  \
    check(condition).ok ? get_ostream() : get_ostream()

#define DO(code)  \
    do { code } while(0)


int main() {
    do { CHECK(2 * 2 == 4); } while(0);
    DO( CHECK(2 * 2 == 4); );
}

这里的[[maybe_unused]]不是关于返回值的,而是关于函数的,以防您更改代码以使其在某些条件下不被使用(此处可能不需要)。

我对你的方法的主要关注是这个声明:

Notice the ternary operator in macro definition. It ensures that a.to_string() is executed only when the assertion fails.

如果不阅读文档,只看 CHECK(a.ok()) << a.to_string();,就会假设 a.to_string() 仅在断言失败时才会执行。

从代码审查或协作的角度来看,这确实是个问题。