#define RETURN(x) result=x;goto CLEANUP
void myfunc() {
int result=0;
if (commserror()) {
RETURN(0);
}
.....
/* On success */
RETURN(1);
CLEANUP:
if (myStruct) { free(myStruct); }
...
return result
}
The advantage being that you never have to remember which things are to be freed at which particular error state. The style also avoids lots of nesting because it returns early. It's not as nice as having defer but it does help in larger functions.Defer available in gcc and clang
111–120 of 262 posts
Re: Defer available in gcc and clang
#112Earlier quoted context omitted.
People manually doing resource cleanup by using goto. I'm assuming that using defer would have prevented the gotos in the first case, and the bug.
Is that true though? Using defer, the code would be: if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) return err; return err; This has the exact same bug: the function exits with a successful return code as long as the SHA hash update succeeds, skipping further certificate validity checks. The fact that resource cleanup has been relegated to defer so that 'goto fail;' can be replaced with 'return err;' f…
Re: Defer available in gcc and clang
#113Earlier quoted context omitted.
This certainly isn't RAII—the term is quite literal, Resource Acquisition Is Initialization, rather than calling code as the scope exits. This is the latter of course, not the former.
People often say that "RAII" is kind of a misnomer; the real power of RAII is deterministic destruction. And I agree with this sentiment; resource acquisition is the boring part of RAII, deterministic destruction is where the utility comes from. In that sense, there's a clear analogy between RAII and defer. But yeah, RAII can only provide deterministic destruction because resource acquisition is initialization. As lo…
Re: Defer available in gcc and clang
#114Earlier quoted context omitted.
People manually doing resource cleanup by using goto. I'm assuming that using defer would have prevented the gotos in the first case, and the bug.
To be fair, there were multiple wrongs in that piece of code: avoiding typing with the forward goto cleanup pattern; not using braces; not using autoformatting that would have popped out that second goto statement; ignoring compiler warnings and IDE coloring of dead code or not having those warnings enabled in the first place. C is hard enough as is to get right and every tool and development pattern that helps avoid…
By putting all the cleanup code at the end of the function after a cleanup label, you have reduced the complexity of resource management: you have one place where the resource is acquired, and one place where the resource is freed. This is actually manageable. Before you return, you check every resource you might have acquired, and if your handle (pointer, file descriptor, PID, whatever) is not in its null state (null pointer, -1, whatever), you call the free function.
By comparison, if you try to put the correct cleanup functions at every exit point, the problem explodes in complexity. Whereas correctly adding a new resource using the 'goto cleanup' pattern requires adding a single 'if (my_resource is not its null value) { cleanup(my_resource) }' at the end of the function, correctly adding a new resource using the 'cleanup at every exit point' pattern requires going through every single exit point in the function, considering whether or not the resource will be acquired at that time, and if it is, adding the cleanup code. Adding a new exit point similarly requires going through all resources used by the function and determining which ones need to be cleaned up.
C is hard enough as it is to get right when you only need to remember to clean up resources in one place. It gets infinitely harder when you need to match up cleanup code with returns.
Re: Defer available in gcc and clang
#115Earlier quoted context omitted.
Is that true though? Using defer, the code would be: if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) return err; return err; This has the exact same bug: the function exits with a successful return code as long as the SHA hash update succeeds, skipping further certificate validity checks. The fact that resource cleanup has been relegated to defer so that 'goto fail;' can be replaced with 'return err;' f…
It would have resulted in an uninitialized variable access warning, though.
Re: Defer available in gcc and clang
#116In C I just used goto - you put a cleanup section at the bottom of your code and your error handling just jumps to it. #define RETURN(x) result=x;goto CLEANUP void myfunc() { int result=0; if (commserror()) { RETURN(0); } ..... /* On success */ RETURN(1); CLEANUP: if (myStruct) { free(myStruct); } ... return result } The advantage being that you never have to remember which things are to be freed at which particular…
Re: Defer available in gcc and clang
#117Earlier quoted context omitted.
Is that true though? Using defer, the code would be: if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) return err; return err; This has the exact same bug: the function exits with a successful return code as long as the SHA hash update succeeds, skipping further certificate validity checks. The fact that resource cleanup has been relegated to defer so that 'goto fail;' can be replaced with 'return err;' f…
It would have resulted in an uninitialized variable access warning, though.
Re: Defer available in gcc and clang
#118Can somebody explain why this is significantly better than using goto pattern? Genuinely curious as I only have a small amount of experience with c and found goto to be ok so far
I feel like C people, out of anyone, should respect the code gen wins of defer. Why would you rely on runtime conditional branches for everything you want cleaned up, when you can statically determine what cleanup functions need to be called? In any case, the biggest advantage IMO is that resource acquisition and cleanup are next to each other. My brain understands the code better when I see "this is how the resource…
Lovely fairy tale. Now can you tell me how you love to scroll back and examine all the defer blocks within a scope when it ends to understand what happens at that point?
Re: Defer available in gcc and clang
#119It’s pedantic, but in the malloc example, I’d put the defer immediately after the assignment. This makes it very obvious that the defer/free goes along with the allocation. It would run regardless of if malloc succeeded or failed, but calling free on a NULL pointer is safe (defined to no-op in the C-spec).
Re: Defer available in gcc and clang
#120In C I just used goto - you put a cleanup section at the bottom of your code and your error handling just jumps to it. #define RETURN(x) result=x;goto CLEANUP void myfunc() { int result=0; if (commserror()) { RETURN(0); } ..... /* On success */ RETURN(1); CLEANUP: if (myStruct) { free(myStruct); } ... return result } The advantage being that you never have to remember which things are to be freed at which particular…
One small nitpick: you don't need check before `free` call, using `free(NULL)` is fine.