问题
I want to use switch statement inside a macro in C. I have the following code segment:
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); \
But, this throws an error message, more specificially:
error: expected expression before ‘switch’
Why is this happening exactly, and what is the proper way to use switch inside a macro in C?
回答1:
you cannot return
from a macro and expect that it behaves like a function. The macro code is expanded literally in your code, so now you have a switch/case & a bunch of return
statements in the last parameter of printf
!
Besides, there's no advantage to use a macro here since you're not using token pasting, stringing or other macros like __FILE__
or __LINE__
in it (as opposed to your THROW_ERROR
macro which uses them).
Instead, define a MESSAGE
(or better: message
) function:
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
}
and pass that to printf
As an aside, wrap your THROW_ERROR
macro within braces since there are 2 statements:
#define THROW_ERROR(err) do { \
fprintf(stderr, "Error in %s:%d: %s.\n", __FILE__, __LINE__, message(err)); \
exit(EXIT_FAILURE); } while(0)
else if you do:
if (fail_code) THROW_ERROR(12);
then only the fprintf
statement is executed when an error occurs, and exit
happens no matter what!
回答2:
You misunderstand the macro in C. It is only the textual replacement.
You need to use function for it:
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 "";
}
you can od course create insane ternary macro:
#define MESSAGE(err) (err == ERROR_NO_MEMORY ? MSG_NO_MEMORY : err == ERROR_INVALID_INDEX ? MSG_INVALID_INDEX : .... )
回答3:
With the expression statement extension (implemented on gcc, clang, and tinycc), you can do:
#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; })
naturally, this isn't "portable" standard C. Portably you could use either an inline function (pretty much unchanged) or a macro with nested ternary expressions:
#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 )
来源:https://stackoverflow.com/questions/52239318/how-to-use-switch-statement-inside-a-macro-in-c