gcc:如何在 variadic_function 中强制存在参数也可能是已知集合中的类型

问题描述

我有以下功能

typedef enum overrideType
{
    ovrBool,ovrFloat,ovrUint32,} overrideType_t;

int override_New(const char name[],overrideType_t type,...);

我希望可选参数列表至少包含一项(如果我能保证它只包含一项就更好了)。是否也可以强加一组允许的类型?

基本上我希望能够编写以下说明:

override_New("a",ovrBool,true);
override_New("b",4.0f);
override_New("c",5,"string"); /* this is tolerable but if it were possible to have a warning would be better */

但应使用以下内容生成错误或至少警告:

override_New("d",ovrFloat);

我对带有 gcc 扩展、属性、使用预处理器功能等的 C11 很好。

解决方法

相反,您的枚举对类型使用标准 printf 格式。然后 gcc 扩展 __attribute__((format(....))) 会警告你。

extern int override_New (const char name[],const char *fmt,...)
    __attribute__ ((format (printf,2,3)));


void foo()
{
    override_New("name","%s");
}
<source>: In function 'foo':
<source>:12:28: warning: format '%s' expects a matching 'char *' argument [-Wformat=]
   12 |     override_New("name","%s");
      |                           ~^
      |                            |
      |                            char *
Compiler returned: 0

https://godbolt.org/z/q4do4r

作为奖励,它还会检查类型。

,

如果传递的参数少于 3 个,则可以使用预处理器 (__VA_ARGS__):

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef enum overrideType
{
    ovrBool,ovrFloat,ovrUint32,} overrideType_t;

#define override_New(a,b,...) override_New(a,__VA_ARGS__)
static int (override_New)(const char name[],overrideType_t type,...)
{
    (void)name;
    (void)type;
    return 0;
}

int main(void)
{
    override_New("a",ovrBool,true); // Ok
    override_New("a",ovrBool);       // Error
    return 0;
}

然后,它会编译失败:

demo.c:23:30: warning: ISO C99 requires at least one argument for the "..." in a variadic macro
   23 |     override_New("a",ovrBool); // fail
      |                              ^
demo.c:12:63: error: expected expression before ‘)’ token
   12 | #define override_New(a,__VA_ARGS__)
      |                                                               ^
demo.c:23:5: note: in expansion of macro ‘override_New’
   23 |     override_New("a",ovrBool); // fail
      |     ^~~~~~~~~~~~
demo.c:24:5: note: in expansion of macro ‘override_New’
   24 |     override_New("a",ovrBool); // fail

(override_New) 周围的括号阻止预处理器扩展函数定义,并允许您对宏和函数本身使用相同的名称。

如果您想要警告而不是错误,请使用 #__VA_ARGS__ gcc 扩展名:

#define override_New(a,#__VA_ARGS__)

返回:

demo.c:23:30: warning: ISO C99 requires at least one argument for the "..." in a variadic macro
   23 |     override_New("a",ovrBool); // fail
      |                              ^

但它可以编译。

无论如何,最好完全避免可变参数,让编译器完成工作并使用 union 和复合文字:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef enum overrideType
{
    ovrBool,} overrideType_t;

typedef union overrideValue
{
    bool as_bool;
    float as_float;
    unsigned as_uint;
} overrideValue_t;

int override_New(const char name[],overrideValue_t value)
{
    (void)name;
    (void)type;
    (void)value;
    return 0;
}

int main(void)
{
    override_New("a",(overrideValue_t){true});           // Ok
    override_New("a",(overrideValue_t){true},"extra");  // Error
    override_New("a",ovrBool);                                    // Error
    return 0;
}

返回:

demo.c:30:5: error: too many arguments to function ‘override_New’
   30 |     override_New("a","extra");  // Error
      |     ^~~~~~~~~~~~
demo.c:19:6: note: declared here
   19 | int (override_New)(const char name[],overrideValue_t value)
      |      ^~~~~~~~~~~~
demo.c:31:5: error: too few arguments to function ‘override_New’
   31 |     override_New("a",ovrBool);                                    // Error
      |     ^~~~~~~~~~~~
demo.c:19:6: note: declared here
   19 | int (override_New)(const char name[],overrideValue_t value)
      |