Elegant error checking - c

Our code (in a simple library implementation) is beginning to look like this:
err = callToUnderlyingLibrary1();
if (err!=0) {
printf ("blah %d\n", err);
...
}
err = callToUnderlyingLibrary2();
if (err!=0) {
printf ("blah %d\n", err);
...
}
err = callToUnderlyingLibrary3();
if (err!=0) {
printf ("blah %d\n", err);
...
}
This is cumbersome and ugly. Is there a better way to do this ? Perhaps using the C preprocessor ? I was thinking something like:
CHECK callToUnderlyingLibrary1();
CHECK callToUnderlyingLibrary2();
CHECK callToUnderlyingLibrary3();
where the CHECK macro invokes the function and does the rudimentary error checking.
Are there preferred idiomatic ways of handling this ?

Another macro-based approach which you can use to mitigate the shortcomings in C fairly easily:
#define CHECK(x) do { \
int retval = (x); \
if (retval != 0) { \
fprintf(stderr, "Runtime error: %s returned %d at %s:%d", #x, retval, __FILE__, __LINE__); \
return /* or throw or whatever */; \
} \
} while (0)
Then to invoke it you have:
CHECK(doSomething1());
CHECK(doSomething2());
// etc.
For bonus points you could easily extend the CHECK macro to take a second argument y that is what to do on failure:
#define CHECK(x, y) do { \
int retval = (x); \
if (retval != 0) { \
fprintf(stderr, "Runtime error: %s returned %d at %s:%d", #x, retval, __FILE__, __LINE__); \
y; \
} \
} while (0)
// We're returning a different error code
CHECK(someFunction1(foo), return someErrorCode);
// We're actually calling it from C++ and can throw an exception
CHECK(someFunction2(foo), throw SomeException("someFunction2 failed")):

Usually, in C, one uses goto for error handling:
int foo()
{
if (Function1() == ERROR_CODE) goto error;
...
struct bar *x = acquire_structure;
...
if (Function2() == ERROR_CODE) goto error0;
...
release_structure(x);
return 0;
error0:
release_structure(x);
error:
return -1;
}
This can be improved with macros and more clever instruction flow (to avoid repeating cleanup code), but I hope you see the point.

I think you should look at exceptions and exception handling. http://www.cplusplus.com/doc/tutorial/exceptions/
try{
callToUnderlyingLibrary1();
callToUnderlyingLibrary2();
callToUnderlyingLibrary3();
}catch(exception& e)
//Handle exception
}
your library functions can throw exceptions if there is an error

Here is a proposition, you may or may not like it:
make your functions return 0 on failure, something else on success
if something fails in your functions, have them set a global (or static) variable to the error code (like errno)
create a die() function that prints the error depending of the error code (or whatever you want it to do)
call your functions with do_something(foo, bar) || die("Argh...");

I prefer a variant of Alexandra C.'s goto-approach:
int foo()
{
int rv = 0;
struct bar *x = NULL;
struct bar *y = NULL;
rv = Function1();
if (rv != OK){
goto error;
}
//...
x = acquire_structure();
if (x==NULL){
rv = ERROR_MEMORY;
goto error;
}
//...
rv = Function2();
if (rv != OK){
goto error;
}
//...
y = acquire_structure();
if (y==NULL){
rv = ERROR_MEMORY;
goto error;
}
//...
rv = release_structure(x);
x = NULL;
if (rv != OK){
goto error;
}
rv = release_structure(y);
y = NULL;
if (rv != OK){
goto error;
}
return OK;
error:
if (x!=NULL){
release_structure(x);
}
return rv;
}
When you use multiple goto-destinations, it is easy to mix them up. Or perhaps you move the initialization of a variable, but forget to update the gotos. And it can be very difficult to test all ways a C-method can fail.
I prefer having a single goto-destination that performs all the cleanup. I find that makes it easier to avoid mistakes.

You could do what you said, which is some rudimentary macro:
#define CHECK(x) (err = x()); \
if (err) { \
printf("blah %d on line %d of file %s\n", err, __LINE__, __FILE__); \
} \
else (void)0
And you could use it like
int err = 0;
CHECK(callToUnderlyingLibrary1); // don't forget the semicolon at the end
CHECK(callToUnderlyingLibrary2);
CHECK(callToUnderlyingLibrary3);

No 'goto', use only 1 'return' in functions. That's the elegant code.
IMHO, OP's question point and all answers are talking about FANCY techniques. Fancy code is just sort of eye candy.

Related

Is there a shorthand for returning conditional integer (error code) in C? That's kinda similar to Go

Is there a shorthand for returning conditional error code from a function in C? From something like:
int e = 0; // Error code.
e = do_something()
if (e)
return e;
// ...rest of the code when no error.
In Go, you can do like:
if err := doSomething(); err != nil {
return err;
}
Why not give it a try:
if ((e = do_something()) != 0) return e;
// ...rest of the code when no error.
This makes it one-lined but not so much clear to read. The operator precedence rule is applied here, so those parenthesis after e and before 0 are obviously necessary.
I like using a macro for that particular case:
#define CHECK_SUCCESS(cmd) \
do \
{ \
int e = cmd; \
if (0 != e) \
return e; \
} while (0);
Then any time you wanna check if your functions succeed:
CHECK_SUCCESS(do_something());
CHECK_SUCCESS(my_func(arg));
Since c++17 you can use init-statements in if's.
if (int e = do_something(); e != 0) {
return e;
}
// ... rest

Bogus 'may be used uninitialized

I am getting an unitialized warning in the following code, and I am stumped trying to figure out why.
I can't see a code path where it is used uninitialized.Can anyone please help?
Also, I could use some adivce on if my gotos are not used well or if there is a cleaner way of doing this.
In function ‘handle_comp_enc’:
fs/compress.c:101:8: warning: ‘write_cdata’ may be used uninitialized in this function [-Wmaybe-uninitialized]
kfree(write_cdata);
Code:
#define ENC (1UL << 1)
#define ZIP (1UL << 2)
#define ENC_ZIP_ENABLED(cmp_enc_flags) ((cmp_enc_flags) & (ENC | ZIP)) == (ENC | ZIP)
int handle_comp_enc(unsigned long comp_enc_flags, unsigned char *read_data,
size_t read_len, unsigned char *write_data, size_t *write_len2) {
unsigned char *write_cdata, *rd_enc_data;
size_t write_clen, enc_src_len;
int err;
if (ENC_ZIP_ENABLED(comp_enc_flags)){
write_cdata = kmalloc(get_compress_fsize(PAGE_SIZE), GFP_KERNEL);
if (!write_cdata) {
err = -ENOMEM;
goto zip_only;
}
}
else if(!(comp_enc_flags & ENC))
write_cdata = write_data;
else{
rd_enc_data = read_data;
enc_src_len = read_len;
goto enc_only;
}
err = do_compress(read_data, read_len, write_cdata, &write_clen);
if (err < 0) {
goto out_enc_zip;
}
if (!(comp_enc_flags & ENC)) {
*write_len2 = write_clen;
goto zip_only;
}
rd_enc_data = write_cdata;
enc_src_len = write_clen;
enc_only:
err = do_skcipher_encrypt(rd_enc_data, enc_src_len, write_data, write_len2);
if (err < 0) {
}
out_enc_zip:
if (ENC_ZIP_ENABLED(comp_enc_flags))
kfree(write_cdata);
zip_only:
return err;
}
Compiler try it's best to produce warning, as the message say "maybe", the compiler don't know that ENC_ZIP_ENABLED(comp_enc_flags) will be false at the label out_enc_zip. Your code don't use an uninitialized value.
That say, I strongly disagree about your use case of goto, your code is unreadable, I take a lot of time just to understand where the code was going.
Your code could be simplified a lot, I'm not sure at 100% that this code have the same behavior as I said your code is hard to read:
#define ENC (1UL << 1)
#define ZIP (1UL << 2)
int handle_comp_enc(unsigned long comp_enc_flags, unsigned char *read_data,
size_t read_len, unsigned char *write_data, size_t *write_len2) {
if ((comp_enc_flags & (ENC | ZIP)) == (ENC | ZIP)) {
unsigned char *tmp = kmalloc(get_compress_fsize(PAGE_SIZE), GFP_KERNEL);
if (!tmp) {
return -ENOMEM;
}
size_t size;
int err = do_compress(read_data, read_len, tmp, &size);
if (!(err < 0)) {
err = do_skcipher_encrypt(tmp, size, write_data, write_len2);
}
kfree(tmp);
return err;
}
else if (!(comp_enc_flags & ENC)) {
return do_compress(read_data, read_len, write_data, write_len2);
}
else {
return do_skcipher_encrypt(read_data, read_len, write_data, write_len2);
}
}
Yeah it looks like a false positive. In your if-else if-else, you only initialize the variable inside the if and else if statements. Apparently you got the tool confused with goto or some such.
But that's not really important, as the origin of the problems is the function design. You don't default initialize variables and you have a tight coupling between memory allocation and the actual algorithm. The use of goto here is ok but it reduces readability somewhat.
I would split this in two functions, where you leave memory handling and error handling to an outer function. Something along the lines of this pseudo code would be much more readable:
int wrapper_function ( ... )
{
unsigned char *write_cdata = NULL;
int err = initialize_me_to_something.
if(ENC_ZIP_ENABLED(comp_enc_flags))
{
write_cdata = kmalloc (...
if(write_cdata == NULL)
{
return -ENOMEM;
}
}
else
{
if(!(comp_enc_flags & ENC)
{
write_cdata = write_data;
...
}
else
{ // some special case
err = do_skcipher_encrypt(...
return err;
}
}
err = do_the_actual_job(write_cdata, otherparameters);
if (err < 0)
{
cleanup();
}
return err;
}
goto is not necessarily evil, but neither are multiple return statements. They are both frowned upon with more or less rational arguments provided. However, multiple return statements tend to improve readability quite a bit over the "on error goto" pattern. Most importantly, they tend to naturally give a better program design with multiple small functions instead of a single big one.
As a side effect, you get rid of some extra branching instructions, which might give a slight performance improvement.

Return error code after first detected error

I have a function which does some initialization and calls other functions, each of which returns an error code. I want to be able to return from this function after the first detected error like this:
int error_code = FirstFunction();
if (error_code != 0) {
return error_code;
}
error_code = SecondFunction();
if (error_code != 0) {
return error_code;
}
// etc...
However, not only does this look rather cumbersome, it also has multiple return statements, and for compliance reasons at my company this is not allowed.
How can I rearrange this so that there is only one return statement, but still stop after the first error code? The only way I can think of is to do nested if statements:
int error_code = FirstFunction();
if (error_code == 0) {
error_code = SecondFunction();
if (error_code == 0) {
error_code = ThirdFunction();
// etc...
}
}
return error_code;
But this could get unreasonable pretty fast. Is there another way to do this?
EDIT: In my program, return code of 0 means success (OK) and non-zero means failure/error (NOT OK)
You don't have to nest all the function calls, the code below do the job as well and should comply with your code writing rules:
error_code = FirstFunction();
if (error_code == 0) {
error_code = SecondFunction();
}
if (error_code == 0) {
error_code = ThirdFunction();
}
// etc...
return error_code;
Here is another lean method that can return different error codes depending on which function fails:
int func(void)
{
int code;
int error_code = (code = FirstFunction()) ? code :
(code = SecondFunction()) ? code :
(code = ThirdFunction()) ? code : 0;
/* ... */
return error_code;
}
Lean and clean (like this one, but avoiding the disliked gotos):
int foo(void)
{
int error_code;
do {
if (0 != (error_code = FirstFunction()))
{
break;
}
if (0 != (error_code = SecondFunction()))
{
break;
}
...
} while (0);
return error_code;
}
This, BTW, follows the more common pattern: 0 is OK, everything else isn't. Adjust as needed)
You could even obfuscate this using a macro:
#define RUN_AND_BREAK_ON_ERROR(rc, f, ...) \
if (0 != (rc = f(__VA_ARGS__))) \
{ \
break; \
}
int foo(void)
{
int error_code;
do {
RUN_AND_BREAK_ON_ERROR(error_code, FirstFunction, <args go here>);
RUN_AND_BREAK_ON_ERROR(error_code, SecondFunction, <args go here>);
...
} while (0);
return error_code;
}
if( (error_code = FirstFunction()) || (error_code = SecondFunction()) || ... ){
return error_code ;
}
return error_code; //denoting no error.
This would return only the first function which returns nonzero. The idea is that for if statement the first function that returns nonzero would short-circuit the whole evaluation and returns the error_code from the function which returned non-zero error_code. Also another thing is value of an assignment statement is the value assigned. That's why this works.
A more easier way would be to sequential if-else
if( error_code = FirstFunction() ) {}
else if( error_code = SecondFunction() ) {}
...
return error_code;
If all these functions take the same type of parameters and have the same return type, you could put them in a function array and iterate over it. When an error is found, it simply breaks out of the loop and returns.
int (*function_array[max_array])();
/*Fill the array with the functions you need*/
for(i=0;i<max_array;i++){
if((error_code=function_array[i]())!=OK){
break;
}
}
return error_code;
(OK is whatever the success return value is for these functions)
Well, there's the one used e.g. in the Linux kernel:
int somefunc(whatever)
{
if (do_something()) {
ret = -EINVAL;
goto err;
}
if (do_something_else()) {
ret = -EPERM;
goto err;
}
/* ... */
ret = 0;
err:
some_mandatory_cleanup();
return ret;
}
But I suspect that's going to be even less well received. (Before you scream, the whole point of that is the mandatory cleanup in the end. The goto arranges it to be executed always, but still puts it out of way.)
Really, I think the code in your first snippet is fine, and the issue is with your guidelines. Even if we only write return error_code; in one place, it's not enough to guarantee that the error code saved in variable is always correct, or that the function completes all cleanup that might be required. (Consider something that allocates memory, and has to release it in any case.)

Is there a way to provide a single macro function to return values of different types including nothing?

I have created the following macros to lock a mutex and return (from the function within which this macro is called) in case the attempt at locking fails. Currently I have narrowed it down to 2 macros - one for returning from functions which do return a value, irrespective of type, and another for returning from functions which return nothing (i.e. void).
The code beyond the macros (below) is for illustration only and has very little to do with the actual production code that the macros will be used in.
#define MUTEX_LOCK()\
{\
if (pthread_mutex_lock(&mutex) != 0)\
{\
printf("Failed to lock mutex.\n");\
return;\
}\
}
#define MUTEX_LOCK_RVAL(err_val)\
{\
if (pthread_mutex_lock(&mutex) != 0)\
{\
printf("Failed to lock mutex.\n");\
return err_val;\
}\
}
void vfunc()
{
printf("\nIn vfunc()\n");
MUTEX_LOCK();
printf("\nOut of vfunc()\n");
}
UINT16 uint16func()
{
printf("\nIn uint16func()\n");
MUTEX_LOCK_RVAL(0);
printf("\nOut of uint16func()\n");
return 9;
}
CHAR* errstr = "Hoo boy!";
CHAR* strfunc()
{
printf("\nIn strfunc()\n");
MUTEX_LOCK_RVAL(errstr);
printf("\nOut of strfunc()\n");
return NULL;
}
Is there a way to reduce these to a single macro that can be used in functions returning a value as well as void.
To make it ANSI compatible I define the macro with return and another simple symbol that evaluates to null and is clearly showing that it is null.
I.e.:
#define VOID_RET //This nulls the return value
#define MUTEX_LOCK(err_val)\
{\
if (pthread_mutex_lock(&mutex) != 0)\
{\
printf("Failed to lock mutex.\n");\
return err_val;\
}\
}
void *mutex = NULL;
void vfunc(void)
{
printf("\nIn vfunc()\n");
MUTEX_LOCK(VOID_RET);
printf("\nOut of vfunc()\n");
}
UINT16 uint16func(void)
{
printf("\nIn uint16func()\n");
MUTEX_LOCK(0);
printf("\nOut of uint16func()\n");
return 9;
}
CHAR* errstr = "Hoo boy!";
CHAR* strfunc(void)
{
printf("\nIn strfunc()\n");
MUTEX_LOCK(errstr);
printf("\nOut of strfunc()\n");
return NULL;
}
I tested this code under C11 and C99.
As far as I can tell from the documentation, you can remove the macro function taking no argument because you are not obligated to pass an argument at all to a macro function expecting arguments.From the GNU GCC documentation:
You can leave macro arguments empty; this is not an error to the
preprocessor [...]
For multiple arguments, this is interesting:
You cannot leave out arguments entirely; if a macro takes two
arguments, there must be exactly one comma at the top level of its
argument list.
With these examples given:
min(, b) ==> (( ) < (b) ? ( ) : (b))
min(a, ) ==> ((a ) < ( ) ? (a ) : ( ))
min(,) ==> (( ) < ( ) ? ( ) : ( ))
min((,),) ==> (((,)) < ( ) ? ((,)) : ( ))
The only solution i can imagine is this:
#define MUTEX_LOCK( err_val )\
{\
{\
if (pthread_mutex_lock(&mutex) != 0)\
{\
printf("Failed to lock mutex.\n");\
return err_val;\
}\
}\
}
int test_int()
{
MUTEX_LOCK( 1 );
return 0;
}
void test_void()
{
MUTEX_LOCK( ; );
return;
}

Simplify and reduce the amount of code without using macros [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
Sometimes, there are situations where the repetition of simple code blocks is unavoidable. To illustrate, with this example code:
Note: this code is for illustration purpose only, real-life code is much bigger and more complex. Also it may contain errors, but the point of this question is not that.
switch(cmd) {
case CMD_BLOCK_READ:
if(current_user != key) {
ERROR("Access violation - invalid key!");
res = CR_ACCESS_DENIED;
break;
}
if(current_state < STATE_BUSY) {
WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
res = CR_NOT_PERMITTED;
break;
}
if(ioctl(fd, HPI_CTL_BR) != 0) {
WARN("Handshake failed (%s). Aborted!", strerror(errno));
res = CR_TIME_OUT;
goto post_resp;
}
if(block_read(id) != 0) {
ERROR("Failed to read %d block (%s)! Aborted!", id, strerror(errno));
res = CR_FAIL;
goto send_nop;
}
res = CR_SUCCESS;
break;
case CMD_BLOCK_WRITE:
if(current_user != key) {
ERROR("Access violation - invalid key!");
res = CR_ACCESS_DENIED;
break;
}
if(current_state < STATE_BUSY) {
WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
res = CR_NOT_PERMITTED;
break;
}
if(ioctl(fd, HPI_CTL_BR) != 0) {
WARN("Handshake failed (%s). Aborted!", strerror(errno));
res = CR_TIME_OUT;
goto post_resp;
}
if(block_write(id) != 0) {
ERROR("Failed to write %d block - %s. Command aborted!", id, strerror(errno));
res = CR_FAIL;
goto send_nop;
}
res = CR_SUCCESS;
break;
case CMD_REQ_START:
if(current_state < STATE_READY) {
WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
res = CR_NOT_PERMITTED;
break;
}
state = STATE_BUSY;
if(ioctl(fd, HPI_CTL_BR) != 0) {
WARN("Handshake failed (%s). Aborted!", strerror(errno));
res = CR_TIME_OUT;
goto send_nop;
}
if(block_read(id) != 0) {
ERROR("Failed to read %d block (%s)! Aborted!", id, strerror(errno));
res = CR_FAIL;
goto post_resp;
}
res = CR_SUCCESS;
break;
}
/* The remaining 28 or so similar commands */
}
As you can see, due to minor differences and the extensive use of break/goto statements, it is not possible to use functions or inlines. What I usually do is define some macros:
/* NOTE: DO NOT USE these macros outside of Big Switch */
#define CHECK_KEY(_key) \
if(current_user != (_key)) \
{ \
ERROR("Access violation!"); \
res = CR_ACCESS_DENIED; \
break; \
}
#define CHECK_STATE(_state) \
if(current_state < _state) \
{ \
WARN("Command %s is not allowed in this state!", cmd_name[cmd]); \
res = CR_NOT_PERMITTED; \
break; \
}
#define HANDSHAKE(_fail) \
if(ioctl(fd, CTL_BR) != 0) \
{ \
WARN("Handshake failed (%s). Aborted!", strerror(errno)); \
res = CR_TIME_OUT; \
goto _fail; \
}
#define BLOCK_READ(_id, _fail) \
if(block_read((int)(_id))!= 0) \
{ \
ERROR("Failed to read %d block (%s)! Aborted!", (int)_id, strerror(errno)); \
res = CR_FAIL; \
goto _fail; \
}
#define BLOCK_WRITE(_id, _fail) \
if(block_write((int)(_id)) != 0) \
{ \
ERROR("Failed to write %d block - %s. Aborted!", (int)_id, strerror(errno)); \
res = CR_FAIL; \
goto _fail; \
}
..and write the same code using them. The code becomes much smaller and (arguably) more readable:
switch(cmd)
{
case CMD_BLOCK_READ:
CHECK_KEY(key);
CHECK_STATE(STATE_BUSY);
HANDSHAKE(post_resp);
BLOCK_READ(id, send_nop);
res = CR_SUCCESS;
break;
case CMD_BLOCK_WRITE:
CHECK_KEY(key);
CHECK_STATE(STATE_BUSY);
HANDSHAKE(post_resp);
BLOCK_WRITE(id, send_nop);
res = CR_SUCCESS;
break;
case CMD_REQ_START:
{
CHECK_STATE(STATE_READY);
state = STATE_BUSY;
HANDSHAKE(send_nop);
BLOCK_READ(id, post_resp);
res = CR_SUCCESS;
break;
}
/* The remaining 28 or so similar commands */
<..>
The code looks more like some kind of scripting language than good old C and is really ugly, but I'm willing to sacrifice that for the sake of readability.
The question is how do you cope with similar situations? What are more elegant solutions and best practises?
P.S. I admit that in general case macros and goto statement is a sign of bad design, so no need to flame about how evil they are or how poor my programming style is.
I'm not going to claim that the Python source code is the paragon of organization, but it contains (IMHO) a good example of macros being used to simplify a complex piece of code.
The Python main loop implements a bytecode-executing stack-based VM. It contains a huge switch-case with one case for every opcode Python supports. The dispatch for an opcode looks like this:
case STORE_ATTR:
w = GETITEM(names, oparg);
v = TOP();
u = SECOND();
STACKADJ(-2);
err = PyObject_SetAttr(v, w, u); /* v.w = u */
Py_DECREF(v);
Py_DECREF(u);
if (err == 0) continue;
break;
where TOP, SECOND and STACKADJ are all defined as macros operating on the stack object . Some macros have alternate #defines used to assist with debugging. All of the opcodes are written in this way, and it helps make the implementation of each opcode much clearer by expressing the logic in this sort of miniature scripting language.
In my view, careful, judicious and limited use of macros can improve code readability and make the logic clearer. In your case, where the macros hide some small but nontrivial functionality, it can be useful to have macros to standardize the implementation and ensure that you don't have multiple copies of the same snippets of code to update.
In such situations I usually consider whether the cases may be reasonably described with a data, which are then processed in a single common block of code. Sure it cannot be done always, but often it is possible.
In your case it might lead to something similar to the following:
#define IO_NOOP 0
#define IO_READ 1
#define IO_WRITE 2
struct cmd_desc {
int check_key; /* non-zero to do a check */
int check_state;
int new_state;
void* handshake_fail;
int io_dir;
void* io_fail;
};
const struct cmd_desc cmd_desc_list[] = {
{ 1, STATE_BUSY, -1, &&post_resp, IO_READ, &&send_nop }, /* CMD_BLOCK_READ */
{ 1, STATE_BUSY, -1, &&post_resp, IO_WRITE, &&send_nop }, /* CMD_BLOCK_WRITE */
{ 0, STATE_READY, STATE_BUSY, &&send_nop, IO_READ, &&post_rep } /* CMD_REQ_START */
};
const struct cmd_desc* cmd_desc = cmds[cmd];
if(cmd_desc->check_key) {
if(current_user != key) {
ERROR("Access violation - invalid key!");
return CR_ACCESS_DENIED;
}
}
if(cmd_desc->check_state != -1) {
if(current_state check_state) {
WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
return CR_NOT_PERMITTED;
}
}
if(cmd_desc->new_state != -1)
state = cmd_desc->new_state;
switch(cmd_desc->io_dir) {
case IO_READ:
if(block_read(id) != 0) {
ERROR("Failed to read %d block (%s)! Aborted!", id, strerror(errno));
res = CR_FAIL;
goto *cmd_desc->io_fail;
}
break;
case IO_WRITE:
if(block_write(id) != 0) {
ERROR("Failed to write %d block (%s)! Aborted!", id, strerror(errno));
res = CR_FAIL;
goto *cmd_desc->io_fail;
}
break;
case IO_NOOP:
break;
}
res = CR_SUCCESS;
Notes I used "Labels as Values" extension of gcc for the goto labels (http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html). In standard C you might use function pointers instead but that would require some reorganization of the code and I do not have enough info for that.
With the code you posted, there is no reason you couldn't have used functions. This would be the "Extract Function" refactoring pattern. To handle the gotos, just leave them in your main function, and call them or not based on the function result.
http://www.refactoring.com/catalog/extractMethod.html
Also, you've really made a mess of things by using variables in the macros that are not passed in. This means you can't reuse them easily and they are arguably worse than writing the whole thing long-hand. If you passed in everything that is used by the macro, then it is more useful. Then you get a duck-typing style coding, which can be used effectively.
Also, you are using C, so you shouldn't "avoid" macros. They are incredibly useful, primarily for code generation. (i.e. stringification and concatentation) Many C++ and some other say "macros are evil". This is C, macros are not evil.

Resources