C动态宏选择
C dynamic macro choosing
我想通过在运行时构建名称来调用宏。我想要一个遵循相同名称规则 ERROR_MSG_X
的宏列表,其中 X 是错误代码并根据变量调用它们。我有以下功能:
void print_error(int e)
{
printf("[ERROR] : code (%d)\n\t%s", e, ERROR_MSG_#e); //not proper syntax
}
header 类似于:
# define ERROR_MSG_1 "Failed to open file.\n"
# define ERROR_MSG_2 "Failed to read file.\n"
# define ERROR_MSG_3 "Failed to execute abc.\n"
...
我试着弄乱 #
和 ##
但没有得到我想要的结果。
我对我能做什么和不能做什么有很多限制,例如没有外部函数、没有多行宏、没有参数化宏、没有全局变量等。有没有办法计算带有传递值的宏,或者您对如何执行此操作有其他建议吗?我可以有一个带有消息的数组并将其传递给函数,但我真的不想那样做,而且我必须按顺序排列错误代码或浪费内存来存储它们。
正如我在评论中所说:
您将加载数组的任务卸载到预处理器,这非常类似于写出数组,但它在函数中占用的行数更少。
void print_error(int e){
const char *errors[] = {ERRORS};
printf("[ERROR] : code (%d)\n\t%s", e, errors[e]);
}
对于 ERRORS
宏:
# define ERRORS "Failed to open file.\n",\
"Failed to read file.\n",\
"Failed to execute abc.\n"
编辑:
我还建议将错误代码放在 enum
中,这样您就可以确定错误的数量并确保不会调用错误的错误,这样做时我通常会编写一个完整的函数来转换错误编码成字符串,但正如你所说,这可能太长了。
你可以:
void print_error(int e) {
const char *p = "";
if (e == 1) p = ERROR_MSG_1;
else if (e == 2) p == ERROR_MSG_2;
else if (e == 3) p == ERROR_MSG_3;
printf("[ERROR] : code (%d)\n\t%s", e, p);
}
或同switch case:
。我会这样做:
void print_error(int e) {
static const char *const strs[] = { ERROR_MSG_1, ERROR_MSG_2, ERROR_MSG_3 };
const char *p = (1 <= e && (e - 1) < sizeof(strs)/sizeof(*strs)) ? strs[e - 1] : "";
printf("[ERROR] : code (%d)\n\t%s", e, p);
}
你可以写一个宏:
#define GET_ERROR_MSG(e) ( \
((e) == 1) ? ERROR_MSG_1 :
((e) == 2) ? ERROR_MSG_2 :
((e) == 3) ? ERROR_MSG_3 :
"")
printf("[ERROR] : code (%d)\n\t%s", e, GET_ERROR_MSG(e));
一个更混乱的宏然后它是值得的:
#define CASE_ERROR_MSG_(e, x) ((e) == (x)) ? ERROR_MSG_##x :
#define GET_ERROR_MSG(e) ( \
CASE_ERROR_MSG_(e, 1) \
CASE_ERROR_MSG_(e, 2) \
CASE_ERROR_MSG_(e, 3)\
"" )
printf("[ERROR] : code (%d)\n\t%s", e, GET_ERROR_MSG(e));
无论哪种方式,您必须以某种方式在一个地方枚举所有消息,以便运行时部分找到它。
宏在编译前展开,所以编译本身没有宏展开。它们在那里是为了节省您的输入,使您的代码更具可读性(有时)并避免容易出错的 C 构造更不容易出错(有时也是),方法是控制宏定义内的所有重复序列并允许您在只有一个地方。但是一旦宏扩展代码进入编译器,就没有宏了。
命题 1:X 宏
可以使用在Linux内核源代码中广泛使用的X macros概念。这个想法是提供一个名为 X
:
的宏的多个扩展
#include <stdio.h>
// The X-macro
#define LIST_OF_ERRORS \
X(ERROR_MSG_1, "Failed to open file.\n") \
X(ERROR_MSG_2, "Failed to read file.\n") \
X(ERROR_MSG_3, "Failed to execute abc.\n")
// 1st definition of X to define enum error codes
#define X(e, m) e,
enum {
LIST_OF_ERRORS
};
#undef X
// 2nd definition of X to define cases in a switch
void print_error(int e)
{
#define X(e, m) case e: printf("[ERROR] : code (%d)\n\t%s", e, m); break;
switch(e) {
LIST_OF_ERRORS
default: printf("[ERROR] : unknown code (%d)\n", e);
}
#undef X
}
int main(void)
{
print_error(ERROR_MSG_2);
print_error(ERROR_MSG_1);
print_error(ERROR_MSG_3);
print_error(10);
return 0;
}
测试:
$ gcc err.c
$ ./a.out
[ERROR] : code (1)
Failed to read file.
[ERROR] : code (0)
Failed to open file.
[ERROR] : code (2)
Failed to execute abc.
[ERROR] : unknown code (10)
N.B.:这部分回答了我使用多行宏时的问题...
命题 2:##
运算符
在此命题中,您需要使用“硬编码”常量错误编号作为参数传递给 PRINT_ERROR()
宏,否则编译器会报错。如果您传递未知错误号,编译器也会抱怨。
#include <stdio.h>
#define ERROR_LABEL_0 "Error message#0\n"
#define ERROR_LABEL_1 "Error message#1\n"
#define ERROR_LABEL_2 "Error message#2\n"
#define ERROR_LABEL_3 "Error message#3\n"
#define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
int main(void)
{
PRINT_ERROR(2);
PRINT_ERROR(1);
PRINT_ERROR(3);
return 0;
}
执行:
$ gcc macro.c
$ ./a.out
[ERROR] : code (2)
Error message#2
[ERROR] : code (1)
Error message#1
[ERROR] : code (3)
Error message#3
但是如果你使用一个超出范围的变量或常量,编译器会报错:
[...]
int main(void)
{
int err = 2;
PRINT_ERROR(2);
PRINT_ERROR(1);
PRINT_ERROR(3);
PRINT_ERROR(err); // <---- Compilation error
PRINT_ERROR(10); // <---- Compilation error
return 0;
}
编译器显示的错误:
$ gcc macro.c
macro.c: In function 'main':
macro.c:15:72: error: 'ERROR_LABEL_err' undeclared (first use in this function); did you mean 'ERROR_LABEL_1'?
15 | #define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
| ^~~~~~~~~~~~
macro.c:25:3: note: in expansion of macro 'PRINT_ERROR'
25 | PRINT_ERROR(err); // <---- Compilation error
| ^~~~~~~~~~~
macro.c:15:72: note: each undeclared identifier is reported only once for each function it appears in
15 | #define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
| ^~~~~~~~~~~~
macro.c:25:3: note: in expansion of macro 'PRINT_ERROR'
25 | PRINT_ERROR(err); // <---- Compilation error
| ^~~~~~~~~~~
macro.c:15:72: error: 'ERROR_LABEL_10' undeclared (first use in this function); did you mean 'ERROR_LABEL_1'?
15 | #define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
| ^~~~~~~~~~~~
macro.c:26:3: note: in expansion of macro 'PRINT_ERROR'
26 | PRINT_ERROR(10); // <---- Compilation error
我想通过在运行时构建名称来调用宏。我想要一个遵循相同名称规则 ERROR_MSG_X
的宏列表,其中 X 是错误代码并根据变量调用它们。我有以下功能:
void print_error(int e)
{
printf("[ERROR] : code (%d)\n\t%s", e, ERROR_MSG_#e); //not proper syntax
}
header 类似于:
# define ERROR_MSG_1 "Failed to open file.\n"
# define ERROR_MSG_2 "Failed to read file.\n"
# define ERROR_MSG_3 "Failed to execute abc.\n"
...
我试着弄乱 #
和 ##
但没有得到我想要的结果。
我对我能做什么和不能做什么有很多限制,例如没有外部函数、没有多行宏、没有参数化宏、没有全局变量等。有没有办法计算带有传递值的宏,或者您对如何执行此操作有其他建议吗?我可以有一个带有消息的数组并将其传递给函数,但我真的不想那样做,而且我必须按顺序排列错误代码或浪费内存来存储它们。
正如我在评论中所说: 您将加载数组的任务卸载到预处理器,这非常类似于写出数组,但它在函数中占用的行数更少。
void print_error(int e){
const char *errors[] = {ERRORS};
printf("[ERROR] : code (%d)\n\t%s", e, errors[e]);
}
对于 ERRORS
宏:
# define ERRORS "Failed to open file.\n",\
"Failed to read file.\n",\
"Failed to execute abc.\n"
编辑:
我还建议将错误代码放在 enum
中,这样您就可以确定错误的数量并确保不会调用错误的错误,这样做时我通常会编写一个完整的函数来转换错误编码成字符串,但正如你所说,这可能太长了。
你可以:
void print_error(int e) {
const char *p = "";
if (e == 1) p = ERROR_MSG_1;
else if (e == 2) p == ERROR_MSG_2;
else if (e == 3) p == ERROR_MSG_3;
printf("[ERROR] : code (%d)\n\t%s", e, p);
}
或同switch case:
。我会这样做:
void print_error(int e) {
static const char *const strs[] = { ERROR_MSG_1, ERROR_MSG_2, ERROR_MSG_3 };
const char *p = (1 <= e && (e - 1) < sizeof(strs)/sizeof(*strs)) ? strs[e - 1] : "";
printf("[ERROR] : code (%d)\n\t%s", e, p);
}
你可以写一个宏:
#define GET_ERROR_MSG(e) ( \
((e) == 1) ? ERROR_MSG_1 :
((e) == 2) ? ERROR_MSG_2 :
((e) == 3) ? ERROR_MSG_3 :
"")
printf("[ERROR] : code (%d)\n\t%s", e, GET_ERROR_MSG(e));
一个更混乱的宏然后它是值得的:
#define CASE_ERROR_MSG_(e, x) ((e) == (x)) ? ERROR_MSG_##x :
#define GET_ERROR_MSG(e) ( \
CASE_ERROR_MSG_(e, 1) \
CASE_ERROR_MSG_(e, 2) \
CASE_ERROR_MSG_(e, 3)\
"" )
printf("[ERROR] : code (%d)\n\t%s", e, GET_ERROR_MSG(e));
无论哪种方式,您必须以某种方式在一个地方枚举所有消息,以便运行时部分找到它。
宏在编译前展开,所以编译本身没有宏展开。它们在那里是为了节省您的输入,使您的代码更具可读性(有时)并避免容易出错的 C 构造更不容易出错(有时也是),方法是控制宏定义内的所有重复序列并允许您在只有一个地方。但是一旦宏扩展代码进入编译器,就没有宏了。
命题 1:X 宏
可以使用在Linux内核源代码中广泛使用的X macros概念。这个想法是提供一个名为 X
:
#include <stdio.h>
// The X-macro
#define LIST_OF_ERRORS \
X(ERROR_MSG_1, "Failed to open file.\n") \
X(ERROR_MSG_2, "Failed to read file.\n") \
X(ERROR_MSG_3, "Failed to execute abc.\n")
// 1st definition of X to define enum error codes
#define X(e, m) e,
enum {
LIST_OF_ERRORS
};
#undef X
// 2nd definition of X to define cases in a switch
void print_error(int e)
{
#define X(e, m) case e: printf("[ERROR] : code (%d)\n\t%s", e, m); break;
switch(e) {
LIST_OF_ERRORS
default: printf("[ERROR] : unknown code (%d)\n", e);
}
#undef X
}
int main(void)
{
print_error(ERROR_MSG_2);
print_error(ERROR_MSG_1);
print_error(ERROR_MSG_3);
print_error(10);
return 0;
}
测试:
$ gcc err.c
$ ./a.out
[ERROR] : code (1)
Failed to read file.
[ERROR] : code (0)
Failed to open file.
[ERROR] : code (2)
Failed to execute abc.
[ERROR] : unknown code (10)
N.B.:这部分回答了我使用多行宏时的问题...
命题 2:##
运算符
在此命题中,您需要使用“硬编码”常量错误编号作为参数传递给 PRINT_ERROR()
宏,否则编译器会报错。如果您传递未知错误号,编译器也会抱怨。
#include <stdio.h>
#define ERROR_LABEL_0 "Error message#0\n"
#define ERROR_LABEL_1 "Error message#1\n"
#define ERROR_LABEL_2 "Error message#2\n"
#define ERROR_LABEL_3 "Error message#3\n"
#define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
int main(void)
{
PRINT_ERROR(2);
PRINT_ERROR(1);
PRINT_ERROR(3);
return 0;
}
执行:
$ gcc macro.c
$ ./a.out
[ERROR] : code (2)
Error message#2
[ERROR] : code (1)
Error message#1
[ERROR] : code (3)
Error message#3
但是如果你使用一个超出范围的变量或常量,编译器会报错:
[...]
int main(void)
{
int err = 2;
PRINT_ERROR(2);
PRINT_ERROR(1);
PRINT_ERROR(3);
PRINT_ERROR(err); // <---- Compilation error
PRINT_ERROR(10); // <---- Compilation error
return 0;
}
编译器显示的错误:
$ gcc macro.c
macro.c: In function 'main':
macro.c:15:72: error: 'ERROR_LABEL_err' undeclared (first use in this function); did you mean 'ERROR_LABEL_1'?
15 | #define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
| ^~~~~~~~~~~~
macro.c:25:3: note: in expansion of macro 'PRINT_ERROR'
25 | PRINT_ERROR(err); // <---- Compilation error
| ^~~~~~~~~~~
macro.c:15:72: note: each undeclared identifier is reported only once for each function it appears in
15 | #define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
| ^~~~~~~~~~~~
macro.c:25:3: note: in expansion of macro 'PRINT_ERROR'
25 | PRINT_ERROR(err); // <---- Compilation error
| ^~~~~~~~~~~
macro.c:15:72: error: 'ERROR_LABEL_10' undeclared (first use in this function); did you mean 'ERROR_LABEL_1'?
15 | #define PRINT_ERROR(e) fprintf(stderr, "[ERROR] : code (%d)\n\t%s", e, ERROR_LABEL_##e)
| ^~~~~~~~~~~~
macro.c:26:3: note: in expansion of macro 'PRINT_ERROR'
26 | PRINT_ERROR(10); // <---- Compilation error