如何在 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;
}