如何在 C 的宏中使用 switch 语句?
How to use switch statement inside a macro in C?
我想在 C 的宏中使用 switch 语句。我有以下代码段:
enum errors {
ERROR_NO_MEMORY,
ERROR_INVALID_INDEX,
ERROR_INVALID_VALUE
};
#define MSG_NO_MEMORY "could not allocate memory"
#define MSG_INVALID_INDEX "index out of bounds"
#define MSG_INVALID_VALUE "invalid value passed as input"
#define MESSAGE(err) \
switch (err) { \
case ERROR_NO_MEMORY: \
return MSG_NO_MEMORY; \
case ERROR_INVALID_INDEX: \
return MSG_INVALID_INDEX; \
case ERROR_INVALID_VALUE: \
return MSG_INVALID_VALUE; \
} \
#define THROW_ERROR(err) \
fprintf(stderr, "Error in %s:%d: %s.\n", __FILE__, __LINE__, MESSAGE(err)); \
exit(EXIT_FAILURE); \
但是,这会抛出一条错误消息,更具体地说:
error: expected expression before ‘switch’
为什么会发生这种情况,在 C 语言的宏中使用 switch 的正确方法是什么?
您不能 return
从宏中期望它的行为像一个函数。宏代码在您的代码中逐字展开,所以现在您在 printf
!
的最后一个参数中有一个 switch/case 和一堆 return
语句
此外,在这里使用宏没有任何优势,因为您没有在其中使用令牌粘贴、字符串或其他宏,如 __FILE__
或 __LINE__
(相对于您的 THROW_ERROR
使用它们的宏)。
相反,定义一个 MESSAGE
(或更好:message
)函数:
const char *message(int code)
{
switch (err) {
case ERROR_NO_MEMORY:
return MSG_NO_MEMORY;
case ERROR_INVALID_INDEX:
return MSG_INVALID_INDEX;
case ERROR_INVALID_VALUE:
return MSG_INVALID_VALUE;
}
return "unknown error"; // just in case no code matches
}
并将其传递给 printf
顺便说一句,将 THROW_ERROR
宏括在大括号内,因为有 2 个语句:
#define THROW_ERROR(err) do { \
fprintf(stderr, "Error in %s:%d: %s.\n", __FILE__, __LINE__, message(err)); \
exit(EXIT_FAILURE); } while(0)
否则如果你这样做:
if (fail_code) THROW_ERROR(12);
那么出错的时候只执行fprintf
语句,exit
无论如何都会发生!
你误解了C中的宏,它只是文本替换。
你需要为此使用函数:
inline const char *MESSAGE(int code)
{
switch (err)
{
case ERROR_NO_MEMORY:
return MSG_NO_MEMORY;
case ERROR_INVALID_INDEX:
return MSG_INVALID_INDEX;
case ERROR_INVALID_VALUE:
return MSG_INVALID_VALUE;
}
return "";
}
你当然可以创建疯狂的三元宏:
#define MESSAGE(err) (err == ERROR_NO_MEMORY ? MSG_NO_MEMORY : err == ERROR_INVALID_INDEX ? MSG_INVALID_INDEX : .... )
使用表达式语句扩展(在 gcc、clang 和 tinycc 上实现),您可以:
#define MESSAGE(err) \
({ int MESSAGE; switch(err){ \
case ERROR_NO_MEMORY: \
MESSAGE = MSG_NO_MEMORY; \
case ERROR_INVALID_INDEX: \
MESSAGE = MSG_INVALID_INDEX; \
case ERROR_INVALID_VALUE: \
MESSAGE = MSG_INVALID_VALUE; \
}; MESSAGE; })
当然,这不是 "portable" 标准 C。可移植地,您可以使用内联函数(几乎没有变化)
或带有嵌套三元表达式的宏:
#define MESSAGE(err) \
( err==ERROR_NO_MEMORY ? MSG_NO_MEMORY \
: err==ERROR_INVALID_INDEX ? MSG_INVALID_INDEX \
: err==ERROR_INVALID_VALUE ? MSG_INVALID_VALUE \
: 0 )
方法有点不同,但您可以利用宏串联。
首先,您必须重新命名您的错误字符串,例如
// note the name is MSG + the error enum symbol
#define MSG_ERROR_NO_MEMORY "could not allocate memory"
#define MSG_ERROR_INVALID_INDEX "index out of bounds"
#define MSG_ERROR_INVALID_VALUE "invalid value passed as input"
然后你可以利用macro concatenation定义宏MESSAGE
为
#define MESSAGE(code) MSG_##code
此宏将 'MSG_' 与作为参数给出的文字相连接。
最后,你可以直接使用你的 THROW_ERROR
#define THROW_ERROR(err) printf("Error %", MESSAGE(err));
THROW_ERROR(ERROR_NO_MEMORY);
// expanded to -> printf("Error %", "could not allocate memory")
所以这是完整的工作示例。
enum errors {
ERROR_NO_MEMORY,
ERROR_INVALID_INDEX,
ERROR_INVALID_VALUE
};
#define MSG_ERROR_NO_MEMORY "could not allocate memory"
#define MSG_ERROR_INVALID_INDEX "index out of bounds"
#define MSG_ERROR_INVALID_VALUE "invalid value passed as input"
#define MESSAGE(code) MSG_##code
#define THROW_ERROR(err) \
fprintf(stderr, "Error in %s:%d: %s.\n", __FILE__, __LINE__, MESSAGE(err)); \
exit(EXIT_FAILURE); \
#include <stdio.h>
#include <stdlib.h>
int main() {
THROW_ERROR(ERROR_NO_MEMORY);
return 0;
}
我想在 C 的宏中使用 switch 语句。我有以下代码段:
enum errors {
ERROR_NO_MEMORY,
ERROR_INVALID_INDEX,
ERROR_INVALID_VALUE
};
#define MSG_NO_MEMORY "could not allocate memory"
#define MSG_INVALID_INDEX "index out of bounds"
#define MSG_INVALID_VALUE "invalid value passed as input"
#define MESSAGE(err) \
switch (err) { \
case ERROR_NO_MEMORY: \
return MSG_NO_MEMORY; \
case ERROR_INVALID_INDEX: \
return MSG_INVALID_INDEX; \
case ERROR_INVALID_VALUE: \
return MSG_INVALID_VALUE; \
} \
#define THROW_ERROR(err) \
fprintf(stderr, "Error in %s:%d: %s.\n", __FILE__, __LINE__, MESSAGE(err)); \
exit(EXIT_FAILURE); \
但是,这会抛出一条错误消息,更具体地说:
error: expected expression before ‘switch’
为什么会发生这种情况,在 C 语言的宏中使用 switch 的正确方法是什么?
您不能 return
从宏中期望它的行为像一个函数。宏代码在您的代码中逐字展开,所以现在您在 printf
!
return
语句
此外,在这里使用宏没有任何优势,因为您没有在其中使用令牌粘贴、字符串或其他宏,如 __FILE__
或 __LINE__
(相对于您的 THROW_ERROR
使用它们的宏)。
相反,定义一个 MESSAGE
(或更好:message
)函数:
const char *message(int code)
{
switch (err) {
case ERROR_NO_MEMORY:
return MSG_NO_MEMORY;
case ERROR_INVALID_INDEX:
return MSG_INVALID_INDEX;
case ERROR_INVALID_VALUE:
return MSG_INVALID_VALUE;
}
return "unknown error"; // just in case no code matches
}
并将其传递给 printf
顺便说一句,将 THROW_ERROR
宏括在大括号内,因为有 2 个语句:
#define THROW_ERROR(err) do { \
fprintf(stderr, "Error in %s:%d: %s.\n", __FILE__, __LINE__, message(err)); \
exit(EXIT_FAILURE); } while(0)
否则如果你这样做:
if (fail_code) THROW_ERROR(12);
那么出错的时候只执行fprintf
语句,exit
无论如何都会发生!
你误解了C中的宏,它只是文本替换。
你需要为此使用函数:
inline const char *MESSAGE(int code)
{
switch (err)
{
case ERROR_NO_MEMORY:
return MSG_NO_MEMORY;
case ERROR_INVALID_INDEX:
return MSG_INVALID_INDEX;
case ERROR_INVALID_VALUE:
return MSG_INVALID_VALUE;
}
return "";
}
你当然可以创建疯狂的三元宏:
#define MESSAGE(err) (err == ERROR_NO_MEMORY ? MSG_NO_MEMORY : err == ERROR_INVALID_INDEX ? MSG_INVALID_INDEX : .... )
使用表达式语句扩展(在 gcc、clang 和 tinycc 上实现),您可以:
#define MESSAGE(err) \
({ int MESSAGE; switch(err){ \
case ERROR_NO_MEMORY: \
MESSAGE = MSG_NO_MEMORY; \
case ERROR_INVALID_INDEX: \
MESSAGE = MSG_INVALID_INDEX; \
case ERROR_INVALID_VALUE: \
MESSAGE = MSG_INVALID_VALUE; \
}; MESSAGE; })
当然,这不是 "portable" 标准 C。可移植地,您可以使用内联函数(几乎没有变化) 或带有嵌套三元表达式的宏:
#define MESSAGE(err) \
( err==ERROR_NO_MEMORY ? MSG_NO_MEMORY \
: err==ERROR_INVALID_INDEX ? MSG_INVALID_INDEX \
: err==ERROR_INVALID_VALUE ? MSG_INVALID_VALUE \
: 0 )
方法有点不同,但您可以利用宏串联。
首先,您必须重新命名您的错误字符串,例如
// note the name is MSG + the error enum symbol
#define MSG_ERROR_NO_MEMORY "could not allocate memory"
#define MSG_ERROR_INVALID_INDEX "index out of bounds"
#define MSG_ERROR_INVALID_VALUE "invalid value passed as input"
然后你可以利用macro concatenation定义宏MESSAGE
为
#define MESSAGE(code) MSG_##code
此宏将 'MSG_' 与作为参数给出的文字相连接。
最后,你可以直接使用你的 THROW_ERROR
#define THROW_ERROR(err) printf("Error %", MESSAGE(err));
THROW_ERROR(ERROR_NO_MEMORY);
// expanded to -> printf("Error %", "could not allocate memory")
所以这是完整的工作示例。
enum errors {
ERROR_NO_MEMORY,
ERROR_INVALID_INDEX,
ERROR_INVALID_VALUE
};
#define MSG_ERROR_NO_MEMORY "could not allocate memory"
#define MSG_ERROR_INVALID_INDEX "index out of bounds"
#define MSG_ERROR_INVALID_VALUE "invalid value passed as input"
#define MESSAGE(code) MSG_##code
#define THROW_ERROR(err) \
fprintf(stderr, "Error in %s:%d: %s.\n", __FILE__, __LINE__, MESSAGE(err)); \
exit(EXIT_FAILURE); \
#include <stdio.h>
#include <stdlib.h>
int main() {
THROW_ERROR(ERROR_NO_MEMORY);
return 0;
}