使用X宏定义匿名枚举遇编译错误及errno字符串化方案咨询
问题:X宏实现errno字符串化时的编译错误及替代方案
开发大型项目时,需要将自定义errno宏转换为字符串用于日志输出,尝试用X宏实现但出现编译错误,相关代码及错误信息如下:
初始代码
// project_errno.h #define PROJECT_ERR_KEY_FAILURE 12222 #define PROJECT_ERR_CIPHER_ZERO_PADDING 12345 #define PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY 14004
改造步骤及代码
- 编写X宏文件:
// project_errno.hx PROJECT_ERR_FUNC(PROJECT_ERR_KEY_FAILURE) 12222 PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING) 12345 PROJECT_ERR_FUNC(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY) 14004
- 转为匿名枚举:
// project_errno.h enum { #define PROJECT_ERR_FUNC(name, value) name=value, #include "project_errno.hx" #undef PROJECT_ERR_FUNC };
- 添加日志用字符串化函数:
// logging.h(声明)及logging.c(定义) const char* stringify_errno(int errno) { switch (errno) { #define PROJECT_ERR_FUNC(name, value) case name: return #value ; #include "project_errno.hx" #undef PROJECT_ERR_FUNC } }
编译错误信息
project_errno.h:8:53: error: expected identifier before numeric constant #define PROJECT_ERR_CIPHER_ZERO_PADDING 12345 ^ ..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’ #define PROJECT_ERR_FUNC(name, value) name=value, ^~~~ ..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345) project_errno.h:8:53: error: expected ‘}’ before numeric constant #define PROJECT_ERR_CIPHER_ZERO_PADDING 12345 ^ ..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’ #define PROJECT_ERR_FUNC(name, value) name=value, ^~~~ ..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345) project_errno.h:8:53: error: expected unqualified-id before numeric constant #define PROJECT_ERR_CIPHER_ZERO_PADDING 12345 ^ ..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’ #define PROJECT_ERR_FUNC(name, value) name=value, ^~~~ ..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345) ^~~~~~~~~~~~~~~~~~~~~~~ In file included from ......../project_errno.h:20:1: error: expected declaration before ‘}’ token }; ^ ..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’ #define PROJECT_ERR_FUNC(name, value) name=value, ^~~~ ..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345) ^~~~~~~~~~~~~~~~~~~~~~~
错误原因分析
- X宏参数格式错误:
project_errno.hx中X宏调用未将名称和值作为两个参数传入,正确写法应为PROJECT_ERR_FUNC(name, value),原写法会导致预处理器解析出非法语法。 - 宏定义冲突:原
project_errno.h中保留了#define宏定义,预处理器会先展开PROJECT_ERR_CIPHER_ZERO_PADDING为12345,导致枚举中出现12345=12345,的非法代码——枚举成员必须是标识符,不能是数字。 - 参数名冲突:字符串化函数使用
errno作为参数名,而errno是系统标准宏,会引发预处理器展开错误,需改用其他名称如err。 - 缺少default分支:switch语句无default分支,无法处理未知错误码,还会触发编译器警告。
修复后的X宏实现代码
1. 修正project_errno.h
删除原有宏定义,仅保留枚举生成逻辑:
// project_errno.h #ifndef PROJECT_ERRNO_H #define PROJECT_ERRNO_H enum { #define PROJECT_ERR_FUNC(name, value) name = value, #include "project_errno.hx" #undef PROJECT_ERR_FUNC }; #endif // PROJECT_ERRNO_H
2. 修正project_errno.hx参数格式
// project_errno.hx PROJECT_ERR_FUNC(PROJECT_ERR_KEY_FAILURE, 12222) PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345) PROJECT_ERR_FUNC(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY, 14004)
3. 修正字符串化函数
// logging.c #include "project_errno.h" const char* stringify_errno(int err) { switch (err) { #define PROJECT_ERR_FUNC(name, value) case name: return #name; #include "project_errno.hx" #undef PROJECT_ERR_FUNC default: return "UNKNOWN_PROJECT_ERROR"; } }
替代方案:数组映射实现
若觉得X宏的预处理器语法过于繁琐,可采用静态数组映射方案,仅需维护一处错误码与字符串的对应关系:
// project_errno.h #ifndef PROJECT_ERRNO_H #define PROJECT_ERRNO_H typedef enum { PROJECT_ERR_KEY_FAILURE = 12222, PROJECT_ERR_CIPHER_ZERO_PADDING = 12345, PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY = 14004, PROJECT_ERR_MAX // 标记数组大小 } ProjectErrno; #endif // PROJECT_ERRNO_H
// logging.c #include "project_errno.h" typedef struct { ProjectErrno code; const char* msg; } ErrMapping; static const ErrMapping err_map[] = { {PROJECT_ERR_KEY_FAILURE, "PROJECT_ERR_KEY_FAILURE"}, {PROJECT_ERR_CIPHER_ZERO_PADDING, "PROJECT_ERR_CIPHER_ZERO_PADDING"}, {PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY, "PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY"} }; const char* stringify_errno(int err) { for (size_t i = 0; i < sizeof(err_map)/sizeof(err_map[0]); i++) { if (err_map[i].code == err) { return err_map[i].msg; } } return "UNKNOWN_PROJECT_ERROR"; }
该方案可读性更强,调试更直观,适合对预处理器语法不熟悉的场景。
内容的提问来源于stack exchange,提问作者danwgh
相关产品推荐
相关产品推荐

