Is it possible to create a macro that expands the following code
int error = 0;
struct parse_tree res = CLEANUP_parse(file_hdl);
// ...
cleanup:
return error
into something semantically equivalent (enough) to
int error = 0;
struct parse_tree res;
struct parse_tree tmp = parse(file_hdl);
if(global_error_variable != 0) {
error = global_error_variable;
goto cleanup;
} else {
res = tmp;
}
// ...
cleanup:
return error
The reason I fear it's impossible is that goto label; is a statement, and I really need an expression. E.g. something like
#define CLEANUP_parse(file_hdl) ((tmp=parse(file_hdl)) ^ tmp ^ global_error_variable) ? goto cleanup : tmp
doesn't work for that reason (and because tmp is not declared and possibly more).
Unfortunately compiler extensions are only possible if GCC, VSC and Texas Instrument's C compiler support them.
We have an existing codebase where each function returns an integer error code and the caller jumps to cleanup: on non-zero return code. I consider introducing functions that use a global error code to enable the use of their return value, but want to do to in a safe and backwards compatible manner. The full picture on SE Code Review
Can I create a macro which conditionally
gotos and serves as an expression?
Not as an expression. Consider a different approach, that requires only a slight refactoring, but will be compatible with standard C:
int error = 0;
struct parse_tree res;
CLEANUP_PARSE(res, file_hdl, error, cleanup);
// ...
cleanup:
return error;
then you would:
#define CLEANUP_PARSE(res, file_hdl, error, cleanup) \
do { \
res = parse(file_hdl); \
if (global_error_variable != 0) { \
error = global_error_variable; \
goto cleanup; \
} \
} while(0)
When GNU extensions are ok, you would also add a GNU version with statement expressions:
#ifdef __GNUC__
// https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
#define CLEANUP_PARSE_RET(file_hdl, ...) \
__extension__({ \
struct parse_tree _res; \
/* macro from above */ \
CLEANUP_PARSE(_res, file_hdl, __VA_ARGS__); \
_res; \
})
#endif
that could be used as you intended:
struct parse_tree res = CLEANUP_PARSE_RET(file_hdl, error, cleanup);
And the user of the library can choose the version he wants, depending on which compiler the user intents to support.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With