GCC下如何为可变参函数强制要求至少一个入参并校验入参类型
C 可变参数函数静态校验实现
原始代码定义
typedef enum overrideType { ovrBool, ovrFloat, ovrUint32, } overrideType_t; int override_New(const char name[], overrideType_t type, ...);
校验规则需求
- 可变参数列表至少传入1个参数,优先做到严格限制仅传入1个参数
- 支持合法调用:
override_New("a", ovrBool, true); override_New("b", ovrFloat, 4.0f); // 多传额外参数的场景可容忍,最好能触发编译警告 override_New("c", ovrUint32, 5, "string"); - 缺失可变参数的非法调用必须拦截,至少触发编译警告:
// 无后续可变参数,必须报错/警告 override_New("d", ovrFloat); - 实现可基于C11标准,结合GCC扩展、编译属性、预处理器特性完成。
实现方案
方案一:基于C11 _Generic 消除可变参数
优先推荐该方案,完全放弃原生可变参数写法,用泛型选择宏分发到固定签名的具体函数,从根源上解决可变参数无静态校验的问题:
// 固定签名的具体实现,无可变参数 int override_New_bool(const char *name, bool val); int override_New_float(const char *name, float val); int override_New_uint32(const char *name, uint32_t val); // 对外泛型宏,固定要求至少传入3个参数:name、type、实际值 #define override_New(name, type, val) _Generic((val), \ bool: override_New_bool, \ float: override_New_float, \ uint32_t: override_New_uint32 \ )(name, val)
该方案的校验效果:
- 缺失可变参数时(比如
override_New("d", ovrFloat)),因为宏定义要求3个参数,直接触发编译错误,无任何额外校验代码 - 参数类型不匹配时,
_Generic找不到对应匹配分支,直接触发编译错误 - 多传参数时,因为宏参数列表固定,同样会直接触发编译错误
如果需要兼容允许多传额外参数的场景,可以把宏调整为支持可变参数的版本,调整后多传的参数会被原样透传到实现逻辑,不会做强制拦截:
#define override_New(name, type, first_val, ...) _Generic((first_val), \ bool: override_New_bool, \ float: override_New_float, \ uint32_t: override_New_uint32 \ )(name, first_val __VA_OPT__(, __VA_ARGS__))
调整后可正常编译多传参数的场景,同时依然能拦截缺失首参数的非法调用。
方案二:保留可变参数,结合GCC扩展做编译期校验
如果因为业务逻辑必须保留原可变参数函数的写法,可以用GCC内置的编译期特性完成校验:
- 隐藏原实现函数,对外用宏包装,强制要求至少传入1个可变参数
- 用
__builtin_va_arg_pack_len()获取编译期可变参数总个数,做参数个数校验 - 用
__builtin_types_compatible_p()结合typeof做参数类型匹配校验
示例代码:
// 原函数实现,保留可变参数逻辑 int override_New_impl(const char *name, overrideType_t type, ...) { va_list ap; va_start(ap, type); // 业务逻辑处理... int ret = 0; va_end(ap); return ret; } // 对外包装宏 #define override_New(name, type, ...) \ _Static_assert( \ __builtin_va_arg_pack_len() >= 1, \ "override_New requires at least 1 variable argument" \ ) \ _Static_assert( \ __builtin_va_arg_pack_len() <= 1, \ "override_New only accepts exactly 1 variable argument" \ ) \ override_New_impl(name, type, __VA_ARGS__)
如果需要对多传参数的场景只给出警告而非直接报错,可以把上述静态断言替换为编译期警告判断,借助GCC的编译分支特性实现。
内容的提问来源于stack exchange,提问作者mastupristi
相关产品推荐
相关产品推荐

