You do not need to use only an expression as the replacement sequence for a macro. It can be a statement (or part of one), such as:
#define check_param(expr) if (!(expr)) return
Since you want the macro to work in functions that return void
and in other functions, we need to give it a way to have a matching return
statement, one that either does or does not give a return value, as desired. We can do this with another parameter:
#define check_param(expr, value) if (!(expr)) return value
Then the macro can be used:
check_param(arg,); // In function that returns void, value is blank.
check_param(arg, -1); // In other functions, value is not blank.
Note that in return value
, value
is not in parentheses. It is usual to enclose macro arguments in parentheses to avoid precedence issues, but that cannot work here because we need return value
to work when value
is blank, and return ();
would cause a syntax error.
Finally, when defining a macro as a statement, there is an idiom to wrap it in a do-while statement so that it acts grammatically like an ordinary statement:
#define check_param(expr, value) do if (!(expr)) return value; while (0)
Note that, in the original if
form, if the macro invocation happens to be followed by an else
, like this:
if (A)
check_param(arg, value);
else
MyRoutine(arg);
then the else
would be associated with the if
resulting from the check_param
macro instead of with the if (A)
. By wrapping the macro in do … while
, we prevent this sort of undesired interpretation.