0x00 前言

在一些c++的项目中常常使用-Werror -Wunused-parameter这样的编译选项,帮助工程师在程序编译阶段,通过不合规的代码检查增强代码的健壮性。

将warning作为error的编译选项对于个人来说,容易被忽略,但是作为工业生产输出的产品尤为重要,这不仅仅是对于程序书写规范的个人能力水平的体现,还是对编程小白初涉产品的一种质量把控。所以很多大型的开源项目会有如此谨慎的操作。

0x01 问题描述

在写代码过程中,会有一些变量声明了,但没有使用。可能是逻辑代码需要暂时注释。比如这两天看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;
}

作者说如果有不够聪明的开发者用了这个歧义的函数会出问题,还不如不去实现具体逻辑。【黑人?】
这样就剩下了两个孤零零的变量,srvurlsrvurllen。如果函数直接return的话,会报错。

当然,这个函数并没有直接返回,而是聪明地使用了本文将要介绍的方法沉默了报错,后面将细说。

0x02 问题复现

程序源码

文件名:test_unuse.cc

 #include <iostream>
 #include <string>
 
 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没有被使用。

出于某种特殊原因(比如好不容易花半天时间给变量起了个名字,发现这个变量没用了,又不忍心删掉),我们需要使用某种方法屏蔽掉这个报错,来保留变量。

0x03 优雅地屏蔽未使用变量报错

思路:
如果我们写一条不做任何事情的语句,欺骗编译器说我用了这个变量。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)?

0x04 优雅地屏蔽多个未使用变量报错

使用模板右值引用初始化一个包含不同类型的数组变量,再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 <iostream>
#include <string>

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

0x05 Tips

  1. 在宏中表示可变参数的变量__VA_ARGS__
  2. 右值引用是提高性能的一种新语法,把局部函数create的对象move出来,而不是copy出来,提高效率。
  3. int a[]{0};用来初始化数组。

0x05 参考文献

https://stackoverflow.com/questions/1486904/how-do-i-best-silence-a-warning-about-unused-variables
https://www.runoob.com/cplusplus/cpp-templates.html

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐