I often write code which ends up being long sequences something like
int error;
error = do_something();
if (error) {
return error;
}
error = do_something_else(with, some, args);
if (error) {
return error;
}
error = do_something_yet_again();
if (error) {
return error;
}
return 0;
I'm searching for a cleaner way to write this that to some extent avoids the repeated identical checks. So far, I've written an ERROR_OR
macro, which works something like
#define ERROR_OR(origerr, newerr) \
({ \
int __error_or_origerr = (origerr); \
(__error_or_origerr != 0) \
? __error_or_origerr \
: (newerr); \
})
which allows the original code to become something like
int error = 0;
error = ERROR_OR(error, do_something());
error = ERROR_OR(error, do_something_else(with, some, args));
error = ERROR_OR(error, do_something_yet_again());
return error;
This is (in my opinion) a little cleaner. It's also less understandable, since the function of the ERROR_PRESERVE
macro isn't apparent unless you read its documentation and/or implementation. It also doesn't solve the problem of repetition, just makes it easier to write all the (now implicit) checks on a single line.
What I'd really like to re-write this all as would be the following:
return ERROR_SHORT_CIRCUIT(
do_something(),
do_something_else(with, some, args),
do_something_yet_again()
);
The hypothetical ERROR_SHORT_CIRCUIT
macro would
- Take a variable number of expressions in its argument list
- Evaluate each expression in order
- If every expression evaluates to zero, evaluate to zero itself
- If any expression evaluates to nonzero, immediately terminate and evaluate to the value of that last expression
This last condition is where my short-circuit diverges from a straightforward use of the ||
operator -- since this will evaluate to 1 instead of the error value.
My initial attempt at writing this is the following:
#define ERROR_SHORT_CIRCUIT(firsterr, ...) \
({ \
int __error_ss_firsterr = (firsterr); \
(__error_ss_firsterr != ERROR_NONE) \
? __error_ss_firsterr \
: ERROR_SHORT_CIRCUIT(__VA_ARGS__); \
})
This has two obvious problems:
- It doesn't handle its base-case (when
__VA_ARGS__
is a single value) - C doesn't support recursive macros
I've looked into some recursive macro hacks, but I dislike using that degree of pre-processor magic -- too much room for something to be subtly wrong. I've also considered using real (possibly variadic) functions, but this would require either
- giving up the short-circuit behavior
- passing the functions in as pointers, and therefore normalizing their signatures
and both of these seem worse than the original, explicit code.
I'm interested to hear advice on the best way to handle this. I'm open to many different approaches, but my ultimate goal is to avoid repetition without hurting readability.
(I suppose it's obvious I'm suffering some envy of the behavior of the ||
operator in languages like Ruby).