【问题标题】:How to use switch statement inside a macro in C?如何在 C 中的宏中使用 switch 语句?
【发布时间】:2018-09-08 20:51:36
【问题描述】:

我想在 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);       \

但是,这会引发错误消息,更具体地说:

错误:“switch”之前的预期表达式

为什么会发生这种情况,以及在 C 中的宏中使用 switch 的正确方法是什么?

【问题讨论】:

  • 这个宏怎么用?
  • @tkausl 请检查已编辑的问题。
  • 宏以`\`结尾:不好
  • 您不能像使用函数一样使用宏。请改用函数。
  • 如果一个函数可以完成这项工作,就不要使用宏。这里没有理由使用宏。这显然是一个 XY 问题。

标签: c switch-statement c-preprocessor


【解决方案1】:

您不能从宏中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

【讨论】:

    【解决方案2】:

    您误解了 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 : .... )
    

    【讨论】:

      【解决方案3】:

      使用表达式语句扩展(在 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; })
      

      当然,这不是“可移植的”标准 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 )
      

      【讨论】:

      • 谢谢你,有趣的方法。
      【解决方案4】:

      这是一种有点不同的方法,但您可以利用宏连接。

      首先,你必须重新命名你的错误字符串,例如

      // 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;
      }
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-02
        相关资源
        最近更新 更多