Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I create a macro which conditionally `goto`s and serves as an expression?

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.

Background

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

like image 396
Perseids Avatar asked Nov 26 '25 00:11

Perseids


1 Answers

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.

like image 177
KamilCuk Avatar answered Nov 27 '25 14:11

KamilCuk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!