First of all, if someone can reword the question to make it clearer, please do.
A common occurrence in C programming is having several resources to be initialized/allocated in a specific order. Each resource is a prerequisite for the initialization of subsequent ones. If one of the steps fails, the leftover resources from previous steps must be de-allocated. Ideal pseudocode (utilizing a magically generic pure-unobtainium clean_up_and_abort() function) would look approximately as follows:
err=alloc_a()
if(err)
clean_up_and_abort()
err=alloc_b()
if(err)
clean_up_and_abort()
err=alloc_c()
if(err)
clean_up_and_abort()
// ...
profit()
I have seen several ways of handling this, all of them seem to have significant drawbacks, at least in terms of what people tend to consider "good practice".
What is are the most readable and least error-prone ways of structuring the code when handling this situation? Efficiency is preferred, but a reasonable amount of efficiency can be sacrificed for the sake of readability. Please list advantages and drawbacks. Answers discussing multiple methods are welcome.
The goal is to hopefully end up with a set of several preferred methods for solving this problem.
I'll start with some of the methods I've already seen, please comment on them and add others.
The three most common methods I've seen so far:
1: Nested if-statements (without multiple returns for the SESE purists). With a long chain of prerequisites, this gets out of hand fast. IMO, even in simple cases this is a readability disaster and has no real advantages. I am including it because I see people do this (too) often.
2: Multiple returns. This is my preferred method right now. THe logic is easy to follow but it's still dangerous because cleanup code has to be duplicated and it's easy to forget to deallocate something in one of the cleanup sections.
3: GOTO. Many people don't like goto on principle, but this is one of the standard arguments for a valid use of goto in C programming. The advantage is that it's hard to forget a cleanup step and there is no copypasting.
Anything else I did not mention?
4: Global variables, woohoo!!! Because everybody loves global variables, just like they love
goto
. But seriously, if you limit the scope of the variables to file scope (using the static keyword) then it's not that bad. Side note: thecleanup
function takes/returns the error code unchanged, so as to declutter the code in theinit_function
.5: Faux OOP. For those who can't handle the truth (that global variables are actually useful in C programs), you can take the C++ approach. C++ takes all of the global variables, puts them into a structure, and calls them "member" variables. And somehow that makes everybody happy.
The trick is to pass a pointer to the structure to all of the functions, as the first argument. C++ does this behind the scenes, in C you have to do it explicitly. I call the pointer
that
so as to avoid conflicts/confusion withthis
.