Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way for exiting a function neatly without using goto in C

We often write some functions which have more than one exit point (that is, return in C). At the same time, when exiting the function, for some general works such as resource cleanup, we wish to implement them only once, rather than implementing them at every exit point. Typically, we may achieve our wish by using goto like the following:

void f() {     ...     ...{..{... if(exit_cond) goto f_exit; }..}..     ...     f_exit:     some general work such as cleanup } 

I think using goto here is acceptable, and I know many people agree on using goto here. Just out of curiosity, does there exist any elegant way for neatly exiting a function without using goto in C?

like image 315
ACcreator Avatar asked Sep 10 '14 13:09

ACcreator


People also ask

What can I use instead of goto in C?

Alternatives to the “goto” are “break” and “continue”.

How can you avoid using goto in C?

Use of goto can be simply avoided using break and continue statements.

What are structured alternatives of goto?

From the early days of programming (i.e. assembler programming) the replacement for goto directives is so-called structured programming. This means using if-then-else statements, for- and while-loops, switch statements, break and continue, etc.

How do you exit a function in C?

The exit() function in C. The exit() function is used to terminate a process or function calling immediately in the program. It means any open file or function belonging to the process is closed immediately as the exit() function occurred in the program.


2 Answers

Why avoid goto?

The problem you want to solve is: How to make sure some common code always gets executed before the function returns to the caller? This is an issue for C programmers, since C does not provide any built in support for RAII.

As you already concede in your question body, goto is a perfectly acceptable solution. Never-the-less, there may be non-technical reasons to avoid using it:

  • academic exercise
  • coding standard compliance
  • personal whim (which I think is what is motivating this question)

There are always more than one way to skin a cat, but elegance as a criteria is too subjective to provide a way to narrow to a single best alternative. You have to decide the best option for yourself.

Explicitly calling a cleanup function

If avoiding an explicit jump (e.g., goto or break) common cleanup code can be encapsulated within a function, and explicitly called at the point of early return.

int foo () {     ...     if (SOME_ERROR) {         return foo_cleanup(SOME_ERROR_CODE, ...);     }     ... } 

(This is similar to another posted answer, that I only saw after I initially posted, but the form shown here can take advantage of sibling call optimizations.)

Some people feel explicitness is more clear, and therefore more elegant. Others feel the need to pass cleanup arguments to the function to be a major detractor.

Add another layer of indirection.

Without changing the semantics of the user API, change its implementation into a wrapper composed of two parts. Part one performs the actual work of the function. Part two performs the cleanup necessary after part one is done. If each part is encapsulated within its own function, the wrapper function has a very clean implementation.

struct bar_stuff {...};  static int bar_work (struct bar_stuff *stuff) {     ...     if (SOME_ERROR) return SOME_ERROR_CODE;     ... }  int bar () {     struct bar_stuff stuff = {};     int r = bar_work(&stuff);     return bar_cleanup(r, &stuff); } 

The "implicit" nature of the cleanup from the point of view of the function that performs the work may be viewed favorably by some. Some potential code bloat is also avoided by only calling the cleanup function from a single place. Some argue that "implicit" behaviors are "tricky", and therefore more difficult to understand and maintain.

Miscellaneous...

More esoteric solutions using setjmp()/longjmp() can be considered, but using them correctly can be difficult. There are open-source wrappers that implement try/catch exception handling style macros over them (for example, cexcept), but you have to change your coding style to use that style for error handling.

One could also consider implementing the function like a state machine. The function tracks progress through each state, an error causes the function to short circuit to the cleanup state. This style is usually reserved for particularly complex functions, or functions that need to be retried later and be able to pick up from where they left off.

Do as the Romans do.

If you need to comply to coding standards, then the best approach is to follow whatever technique is most prevalent in the existing code base. This applies to almost all aspects of making changes to an existing stable source code base. It would be considered disruptive to introduce a new coding style. You should seek approval from the powers that be if you feel a change would dramatically improve some aspect of the software. Otherwise, as "elegance" is subjective, arguing for the sake of "elegance" is not going to get you anywhere.

like image 117
jxh Avatar answered Oct 28 '22 12:10

jxh


For example

void f() {     do     {          ...          ...{..{... if(exit_cond) break; }..}..          ...     }  while ( 0 );      some general work such as cleanup } 

Or you could use the following structure

while ( 1 ) {    //... } 

The main advantage of the structural approach contrary to using goto statements is that it introduces a discipline in writing code.

I am sure and have enough experience that if a function has one goto statement then through some time it will have several goto statements.:)

like image 36
Vlad from Moscow Avatar answered Oct 28 '22 13:10

Vlad from Moscow