在一些c++的项目中常常使用-Werror -Wunused-parameter这样的编译选项,帮助工程师在程序编译阶段,通过不合规的代码检查增强代码的健壮性。
将warning作为error的编译选项对于个人来说,容易被忽略,但是作为工业生产输出的产品尤为重要,这不仅仅是对于程序书写规范的个人能力水平的体现,还是对编程小白初涉产品的一种质量把控。所以很多大型的开源项目会有如此谨慎的操作。
在写代码过程中,会有一些变量声明了,但没有使用。可能是逻辑代码需要暂时注释。比如这两天看cve-2019-5544这个洞的源码时,发现slp定义了一个检测url合法性的函数SLPCheckServiceUrlSyntax
,但是进去之后发现是介样子的。
int SLPCheckServiceUrlSyntax(const char * srvurl, size_t srvurllen)
{
(void)srvurl;
(void)srvurllen;
/*!@todo Do we actually need to do something here to ensure correct
* service-url syntax, or should we expect that it will be used
* by smart developers who know that ambiguities could be encountered
* if they don't?
if (srvurllen < 8)
return 1;
if (strncasecmp(srvurl, "service:",8))
return 1;
return 0;
*/
return 0;
}
作者说如果有不够聪明的开发者用了这个歧义的函数会出问题,还不如不去实现具体逻辑。【黑人?】
这样就剩下了两个孤零零的变量,srvurl
和 srvurllen
。如果函数直接return的话,会报错。
当然,这个函数并没有直接返回,而是聪明地使用了本文将要介绍的方法沉默了报错,后面将细说。
文件名:test_unuse.cc
#include
#include
using namespace std;
void bar(int a)
{
cout <<"a: "<<a<<endl;
}
void foo(int param1, int param2, string a)
{
bar(param1);
}
int main()
{
int i = 39;
int j = 20;
foo(i,j,"aa");
return 0;
}
~ test_unuse g++ -O0 test_unuse.cc -Werror -Wunused-parameter -std=c++11
test_unuse.cc:28:26: error: unused parameter ‘param2’ [-Werror=unused-parameter]
void foo(int param1, int param2, string a)
^
test_unuse.cc:28:41: error: unused parameter ‘a’ [-Werror=unused-parameter]
void foo(int param1, int param2, string a)
^
cc1plus: all warnings being treated as errors
报错,原因是foo的参数param2和a没有被使用。
出于某种特殊原因(比如好不容易花半天时间给变量起了个名字,发现这个变量没用了,又不忍心删掉),我们需要使用某种方法屏蔽掉这个报错,来保留变量。
思路:
如果我们写一条不做任何事情的语句,欺骗编译器说我用了这个变量。unused parameter
的报错是不是就不会出现了?
简单地说,像我们常在if条件判断时常用的空语句;
表示不做任何事。我们能不能使用这个变量执行空操作吗?
使用强制类型转换,将变量转换成void类型,将是我们最好的选择(C 和 C++通用)。
(void)param1;
或者使用宏
#define UNUSED(expr) do { (void)(expr); } while (0)
...
void foo(int param1, int param2)
{
UNUSED(param2);
bar(param1);
}
Qt有个宏Q_UNUSED
就是使用了这种技巧。
那么,新的问题来了,请接招。
上面介绍的方法可以屏蔽1个变量,如果想屏蔽100个变量,能不能不写100个(void)
?
使用模板右值引用初始化一个包含不同类型的数组变量,再void这个变量。
简单地说,不管变量是什么类型,我摸黑直接用这些变量去初始化一个数组,统一转换这个数组变量就行了。说实在的,不同变量能放入到一个数组中,真的颠覆了我对C++的认知能力。但是事实如此,只不过这个数组变量不能正常使用。
// The USE(x, ...) template is used to silence C++ compiler warnings
// issued for (yet) unused variables (typically parameters).
// The arguments are guaranteed to be evaluated from left to right.
struct Use {
template <typename T>
Use(T&&) {
} // NOLINT(runtime/explicit)
};
#define USE(...) \
do { \
::v8::base::Use unused_tmp_array_for_use_macro[]{__VA_ARGS__}; \
(void)unused_tmp_array_for_use_macro; \
} while (false)
验证使用
#include
#include
using namespace std;
template <typename T>
inline T const& Max(T const& a, T const& b)
{
return a<b?b:a;
}
struct Use {
template <typename T>
Use(T&&) {
} // NOLINT(runtime/explicit)
};
#define USE(...) \
do { \
Use unused_tmp_array_for_use_macro[]{__VA_ARGS__}; \
(void)unused_tmp_array_for_use_macro; \
} while (false)
void bar(int a)
{
cout <<"a: "<<a<<endl;
}
#define UNUSED(expr) do { (void)(expr); } while (0)
void foo(int param1, int param2, string a)
{
USE(param2, a);
bar(param1);
}
int main()
{
int i = 39;
int j = 20;
foo(i,j,"aa");
return 0;
}
执行编译,无报错
➜ test_unuse g++ -O0 test_unuse.cc -Werror -Wunused-parameter -std=c++11
__VA_ARGS__
;int a[]{0};
用来初始化数组。https://stackoverflow.com/questions/1486904/how-do-i-best-silence-a-warning-about-unused-variables
https://www.runoob.com/cplusplus/cpp-templates.html