【问题标题】:Variadic UNUSED function/macro可变参数 UNUSED 函数/宏
【发布时间】:2014-06-07 18:59:09
【问题描述】:

抑制未使用变量的 C 编译器警告的一种众所周知且可移植的方法是(请参阅unused parameter warnings in C code):

#define UNUSED(x) (void)(x)

我正在寻找一种方法来概括它以获取多个输入(不同类型):

void foo(int a, long b, void* c){

   /* Want this: */
   ALL_UNUSED(a, b, c);

   /* instead of: */
   UNUSED(a);
   UNUSED(b);
   UNUSED(c);
}

似乎可以解决问题的一种方法是使用可变参数函数

static inline void ALL_UNUSED(int dummy, ...) {}

但是,我怀疑这种解决方案在专家眼中是令人反感的。

是否有一种符合标准且可移植(即不使用__attribute__((unused)))的方式来制作可变参数 UNUSED() 函数/宏?非常感谢!

编辑

在 C99 或 C 预处理器的上下文中,似乎不存在一种干净的方式来执行我所要求的操作。这就是生活。

在下面的回答中,@Dabo 展示了一种非常有趣的方式来执行我要求使用一系列宏的操作。这是整洁且内容丰富的(至少对我而言),所以我接受这个答案。也就是说,我不会将它部署在一个大项目中,因为它的眼泪足以超过它带来的好处(在我看来)。但人们在这里会得出不同的结论。

如下所述,使用空可变参数函数的方法也不是完美的。虽然它是一个非常优雅的单行代码,但它会引发有关未初始化变量的警告(如果是的话)。此外,你必须相信你的编译器会完全优化它,我原则上反对这一点,但我尝试过的所有编译器实际上都是这样做的。

一个相关案例是在早期的高级接口设计阶段之后对函数进行存根。然后您未使用的变量将全部是函数参数并按定义进行初始化,以下方法可以正常工作

static inline void UNUSED(int dummy, ...) {}

void foo(int a, long b, void* c){
    UNUSED(a, b, b); /* No warnings */
}

【问题讨论】:

  • #define UNUSED(...) (void)(__VA_ARGS__)怎么样。
  • 很遗憾没有——试过了。编译器将警告未使用的表达式结果或“作为语句的值”。语义上,你期望什么 (void) (a, b, c);是什么意思?
  • 感谢您指出这一点。尝试其他方式...
  • 谁能详细说明可变参数函数的问题?
  • 注意 gcc 有 -Wno-unused-variable 选项,这似乎符合您的要求?虽然也许问题更多的是关于 varadics 和宏...

标签: c macros c-preprocessor


【解决方案1】:

基于这两个帖子Variadic macro to count number of argumentsOverloading macros我做了以下

#define UNUSED1(x) (void)(x)
#define UNUSED2(x,y) (void)(x),(void)(y)
#define UNUSED3(x,y,z) (void)(x),(void)(y),(void)(z)
#define UNUSED4(a,x,y,z) (void)(a),(void)(x),(void)(y),(void)(z)
#define UNUSED5(a,b,x,y,z) (void)(a),(void)(b),(void)(x),(void)(y),(void)(z)

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5, N,...) N
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1)

#define ALL_UNUSED_IMPL_(nargs) UNUSED ## nargs
#define ALL_UNUSED_IMPL(nargs) ALL_UNUSED_IMPL_(nargs)
#define ALL_UNUSED(...) ALL_UNUSED_IMPL( VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__ )

可以如下使用什么

 int main()
 {
    int a,b,c;
    long f,d;

    ALL_UNUSED(a,b,c,f,d);

    return 0;
  }

eclipse 宏扩展给出:

  (void)(a),(void)(b),(void)(c),(void)(f),(void)(d)

使用gcc -Wall 编译,没有警告

编辑:

#define UNUSED1(z) (void)(z)
#define UNUSED2(y,z) UNUSED1(y),UNUSED1(z)
#define UNUSED3(x,y,z) UNUSED1(x),UNUSED2(y,z)
#define UNUSED4(b,x,y,z) UNUSED2(b,x),UNUSED2(y,z)
#define UNUSED5(a,b,x,y,z) UNUSED2(a,b),UNUSED3(x,y,z)

EDIT2

至于你发的inline方法,快速测试一下

int a=0;
long f,d;

ALL_UNUSEDINLINE(a,f,&d);

给出‘f’ is used uninitialized in this function [-Wuninitialized] 警告。所以这里至少有一个用例打破了这种方法的普遍性

【讨论】:

  • 相当不错!我会玩这个,看看感觉如何。手动枚举似乎在所难免,但很难让人爱上 cpp。谢谢!
  • 我能问一下您对我在帖子中提到的 void 可变参数函数方法的看法(这比您令人印象深刻的宏解决方案短一些)吗?你觉得它不正确和/或有缺陷吗?
  • @dag 查看我关于inline 的编辑2,我认为它回答了您的问题。
  • 感谢您指出这一点!我想到的(未说明的)用例实际上只是为了抑制未使用的函数参数的警告。
【解决方案2】:

我采用了 Dabo 的 (https://stackoverflow.com/a/23238813/5126486) 很棒的解决方案并进行了一些改进,因此更容易扩展到 5 个以上:

#define UNUSED0()
#define UNUSED1(a)                  (void)(a)
#define UNUSED2(a,b)                (void)(a),UNUSED1(b)
#define UNUSED3(a,b,c)              (void)(a),UNUSED2(b,c)
#define UNUSED4(a,b,c,d)            (void)(a),UNUSED3(b,c,d)
#define UNUSED5(a,b,c,d,e)          (void)(a),UNUSED4(b,c,d,e)

#define VA_NUM_ARGS_IMPL(_0,_1,_2,_3,_4,_5, N,...) N
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(100, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0 )

#define ALL_UNUSED_IMPL_(nargs) UNUSED ## nargs
#define ALL_UNUSED_IMPL(nargs) ALL_UNUSED_IMPL_(nargs)
#define ALL_UNUSED(...) ALL_UNUSED_IMPL( VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__ )

【讨论】:

  • 不处理 0 可变参数...但很容易解决。我会编辑...
【解决方案3】:

您对此有何看法:

#define UNUSED(...) [__VA_ARGS__](){};

例子:

void f(int a, char* b, long d)
{
    UNUSED(a, b, d);
}

应扩展为 lambda 定义:

[a,b,d](){}; //optimized by compiler (I hope!)

===== 用http://gcc.godbolt.org 测试===== 我试过这段代码:

#define UNUSED(...) [__VA_ARGS__](){};

int square(int num, float a) {
  UNUSED(a);
  return num * num;
}

结果输出(使用 -O0 -Wall 编译)是:

square(int, float):
    pushq   %rbp
    movq    %rsp, %rbp
    movl    %edi, -4(%rbp)
    movss   %xmm0, -8(%rbp)
    movl    -4(%rbp), %eax
    imull   -4(%rbp), %eax
    popq    %rbp
    ret

编辑:

如果您可以使用 C++11,这可能是一个更好的解决方案:

template <typename ...Args>
void UNUSED(Args&& ...args)
{
    (void)(sizeof...(args));
}

【讨论】:

  • 这个问题是关于 C,而不是 C++11。
  • 导致“警告:表达式结果未使用”,但这可能很容易修复
  • user824425 的 +1 评论和 Gian Lorenzo Meocci 的 -1 答案。问题是关于 C 的。如果是 C++,您可以简单地省略参数名称(但保留类型)。
  • lambda 要求对象是可复制的。
【解决方案4】:

这是一种非常简单的方法,不需要任何特殊技巧,也不会产生任何运行时开销。

原始答案(仅限 C++)

#define UNUSED(...) (void)sizeof(__VA_ARGS__)

https://godbolt.org/z/Gz8MfvaTz

更新答案(C 和 C++)

我忘记将编译器从 C++ 更改为 C,因此忘记了修改后的答案。

int main(int argc, char ** argv)
{
    /* different types */
    UNUSED(argc, argv);

    /* many variables */
    int w, x, y, z;
    UNUSED(w, x, y, z);

    /* single variable */
    void * ptr;
    UNUSED(ptr);
}

MSVC
/O2 /W4
没有来自 C 和 C++ 编译器的警告。

叮当
-O3 -Wall -Wextra
没有来自 C 和 C++ 编译器的警告。

GCC
-O3 -Wall -Wextra
仅来自 C++ 编译器没有警告。 对于 C 编译器,仅忽略最后一个参数而不发出警告。所以在最坏的情况下,这个版本的UNUSED 与传统的#define UNUSED(x) ((void)x) 相当。

【讨论】:

    【解决方案5】:

    您可以使用编译时__VA_ARGS__ 宏。

    #define UNUSED(...) (void)(__VA_ARGS__)

    更新:经过大量试验,我想出了一个优化的解决方案:

    #define UNUSED(...)  __VA_ARGS__
    
    int main()
    {
        int e, x;
        char **a, **b, *c, d[45];
        x = x, UNUSED(a, b, c, d, e), x; 
    
        return 0;
    }
    

    注意事项:

    1. 它并没有完全消除警告,而是将它们减少3 相同类型的警告:
      warning: value computed is not used

    2. 第一个和最后一个x 确保分配相同的数据类型。

    3. 我会说它是经过优化的,因为对于任何数量的未使用变量,它都会发出 3 警告(我可能错了,请自己测试,如果你得到更多,请报告我)和代码量(实现它所需的宏操作)更少。

    4. 我还在努力,如果我找到更好的解决方案会发布。

    【讨论】:

    • 感谢@MadHatter,但我认为 Clang 和 GCC 仍然会发出警告。 GCC“警告:逗号表达式的左侧操作数无效[-Wunused-value]”,Clang:“警告:表达式结果未使用[-Wunused-value]”
    • 耶!!我自己用gcc -Wall 编译它并得到同样的警告。还在尝试……
    猜你喜欢
    • 1970-01-01
    • 2020-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    相关资源
    最近更新 更多