Как использовать оператор switch внутри макроса в C? - PullRequest
0 голосов
/ 08 сентября 2018

Я хочу использовать оператор switch внутри макроса на C. У меня есть следующий сегмент кода:

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"

Почему это происходит именно так, и как правильно использовать switch внутри макроса в C?

Ответы [ 3 ]

0 голосов
/ 08 сентября 2018

Вы неправильно поняли макрос в 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 : .... )
0 голосов
/ 09 сентября 2018

С расширением оператора выражения (реализованным в 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 )
0 голосов
/ 08 сентября 2018

вы не можете return из макроса и ожидать, что он ведет себя как функция. Код макроса раскрывается буквально в вашем коде, так что теперь у вас есть переключатель / регистр и набор return операторов в последнем параметре printf!

Кроме того, здесь нет смысла использовать макрос, так как вы не используете в нем вставку токенов, нанизывание или другие макросы, такие как __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 происходит независимо от того, что!

...