Live data from Hacker News

Error handling style in C

pixelstech.net

21–30 of 45 posts

Re: Error handling style in C

#23
post #18

Earlier quoted context omitted.

Why should the goto be to one single target? Multiple goto statements are good for multiple clean ups without adding indentation levels and without having artificially long logic ands. For example: int init_abc() { if (!init_a()) goto err_a; if (!init_b()) goto err_b; if (!init_c()) goto err_c; return 1; err_c: cleanup_b(); err_b: cleanup_a(); err_a: return 0; } seems to be the cleanest way to do what it does in C. F…

I guess it's fine to use multiple targets too. However, usually you can get away with one, because free(NULL) and similar cleanups tend to be no-ops. So you have something like: char *foo = 0, *bar = 0; if((foo = malloc(X)) == NULL || (bar = malloc(Y)) == NULL) goto cleanup; make_me_millions(foo, bar); cleanup: free(bar); free(foo); In this case, and many cases like it, there's no need to have two jump targets, becau…

because free(NULL) and similar cleanups tend to be no-ops. So you have something like

You really need to check the specification on each function. free is defined that free(NULL) is no-op, but there are other things where that is not the case. Also, that code is not portable since NULL does not have to be 0.

Re: Error handling style in C

#25
post #3

It lacks 4th method that I find the best: with long jumps. I mean for example: jmp_buf errbuf; int result; if( !( result = set_jmp ) ) { /* Code to do on fail. Result may be some error code */ } else { someaction( par1, par2, ..., errbuf); anotheraction( par1, par2, ..., errbuf); /* Etc. */ } errbuf might be global if you prefer, but I'd rather avoid them. When something is wrong called function calls longjmp(errbuf,…

A longjmp is just a fancy goto. If it's all local to the function, just use goto.

Re: Error handling style in C

#26
post #3

It lacks 4th method that I find the best: with long jumps. I mean for example: jmp_buf errbuf; int result; if( !( result = set_jmp ) ) { /* Code to do on fail. Result may be some error code */ } else { someaction( par1, par2, ..., errbuf); anotheraction( par1, par2, ..., errbuf); /* Etc. */ } errbuf might be global if you prefer, but I'd rather avoid them. When something is wrong called function calls longjmp(errbuf,…

A longjmp is just a fancy goto. If it's all local to the function, just use goto.

That's more than goto:

1. It can be called from any place in code.

2. Its call can be written in function. You don't write so much ifs whenever you call it.

Re: Error handling style in C

#27
As with anything else, a balance of these is most useful.

If there is nothing to clean up, error checks and early exit. But only if it is one nested level deep. Any more and someone reading your code may miss it.

When there is a stack of things to be cleaned up use multiple goto targets at the end and fall through them when everything is ok.

Try to keep the nesting to a minimum. I used to indent my C with 2 spaces. Now that I use mainly use Python I have switched to 4 spaces in C and it helps keep things readable and reminds me not to get too deeply nested.

Mix and match as needed. But I think the most important thing is to keep the nesting to a minimum, that makes it much easier for those who come after you to maintain things. If you must have deep nesting or complicated logic at least make sure you do a good job of commenting what it is supposed to be doing.

Re: Error handling style in C

#28
post #18

Earlier quoted context omitted.

I guess it's fine to use multiple targets too. However, usually you can get away with one, because free(NULL) and similar cleanups tend to be no-ops. So you have something like: char *foo = 0, *bar = 0; if((foo = malloc(X)) == NULL || (bar = malloc(Y)) == NULL) goto cleanup; make_me_millions(foo, bar); cleanup: free(bar); free(foo); In this case, and many cases like it, there's no need to have two jump targets, becau…

because free(NULL) and similar cleanups tend to be no-ops. So you have something like You really need to check the specification on each function. free is defined that free(NULL) is no-op, but there are other things where that is not the case. Also, that code is not portable since NULL does not have to be 0.

Just a correction: the code is actually perfectly portable. The integer constant 0 is the canonical definition of the null pointer by definition in the standard (See Section 6.2.2.3 "Pointers" in C89). The null pointer constant (NULL) is defined primarily for convenience (so a reader knows you mean a null pointer instead of a arithmetic zero). Of course, the bitwise representation of the null pointer need not be all-bits-zero; that is, NULL = (void )0 != ((int *)&0).

Re: Error handling style in C

#29
a related question--how do people generally build up decorated strerror messages? e.g. in some versions of ls, "ls foo" (when foo doesn't exist) will print "ls: stat: foo: No such file or directory". how is that string built up?

Re: Error handling style in C

#30
post #28

Earlier quoted context omitted.

because free(NULL) and similar cleanups tend to be no-ops. So you have something like You really need to check the specification on each function. free is defined that free(NULL) is no-op, but there are other things where that is not the case. Also, that code is not portable since NULL does not have to be 0.

Just a correction: the code is actually perfectly portable. The integer constant 0 is the canonical definition of the null pointer by definition in the standard (See Section 6.2.2.3 "Pointers" in C89). The null pointer constant (NULL) is defined primarily for convenience (so a reader knows you mean a null pointer instead of a arithmetic zero). Of course, the bitwise representation of the null pointer need not be all-…

I stand corrected, it does define the integer constant 0 to be promoted to the null pointer. In C11 this is Section 6.3.2.3. To make it even more confusing Section 7.19 Common Definitions defines NULL to be an implementation depefined null pointer constant.

I was always taught that (void *)0 is a valid definition of NULL, but that 0 was not necessarily.

Post reply on HN