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 在任何一种情况下都不会发出警告。
两个问题:
- 这种行为是否有任何原因,或者我应该将其作为 clang bug 归档?
- 如何在不完全禁用“-Wunused-value”的情况下抑制它?我试过
[[maybe_unused]]
和 __attribute__((unused))
但它们似乎对语句不起作用。
我认为这不是我在这里建议的好的解决方案,但您可以更改代码,以便始终使用 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()
仅在断言失败时才会执行。
从代码审查或协作的角度来看,这确实是个问题。
我使用了一个名为 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 在任何一种情况下都不会发出警告。
两个问题:
- 这种行为是否有任何原因,或者我应该将其作为 clang bug 归档?
- 如何在不完全禁用“-Wunused-value”的情况下抑制它?我试过
[[maybe_unused]]
和__attribute__((unused))
但它们似乎对语句不起作用。
我认为这不是我在这里建议的好的解决方案,但您可以更改代码,以便始终使用 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()
仅在断言失败时才会执行。
从代码审查或协作的角度来看,这确实是个问题。