various exits from function with one general clean-up?

F

Felix Kater

Hi,

when I need to execute a general clean-up procedure (inside of a
function) just before the function returns -- how do I do that when
there are several returns spread over the whole function?

My first approach: Use "while(1)" and "break", however this doesn't work
if there is another loop inside (since I can't break two loops at the
same time):

void f(){
while(1){

if ...

break; /* first return: */

switch(...){
case ...:

break; /* second return (doesn't work): */
}

/* other code */

break; /* break in any case */
}
/* clean up procedure */
}


Could someone solve this -- or maybe provide a better solution in
general?

Thanks
Felix
 
D

dandelion

Felix Kater said:
Hi,

when I need to execute a general clean-up procedure (inside of a
function) just before the function returns -- how do I do that when
there are several returns spread over the whole function?

Then don't do that...
Could someone solve this -- or maybe provide a better solution in
general?

Hint A) Use a 'goto'. This is one of the *very* few occasions where I would
advice this.
Hint B) Keep a 'status' local var and check it for every step, return status
*once*.
Hint C) Abstract 'cleanup' into a specialized function. Call it when
appropriate.
 
R

Richard Tobin

Felix Kater said:
My first approach: Use "while(1)" and "break"

The result would be completely unreadable even if it worked. Use a
straightforward goto instead.

-- Richard
 
C

Chris Croughton

when I need to execute a general clean-up procedure (inside of a
function) just before the function returns -- how do I do that when
there are several returns spread over the whole function?

I assume that you mean cleaning up mess local to the function, rather
than global mess (the latter is best done with another function called
on exit).
My first approach: Use "while(1)" and "break", however this doesn't work
if there is another loop inside (since I can't break two loops at the
same time):

No, it doesn't. "Labelled break" would be useful.
Could someone solve this -- or maybe provide a better solution in
general?

One way is to restructure the code so that there is only one exit, using
if/else etc. and storing the result for the exit. Another (which can be
combined) is to use status flags and/or state variables.

Or you can use goto, which is in many cases the cleanest solution
especially for error traps. If a function has one label at the bottom,
clearly marked and commented, and the only thing it does is cleanup and
exit, it can be easily understood and maintained (better than
duplicating code for each return or having a baroque system of flags).

Chris C
 
E

Eric Sosman

Chris said:
[...]
One way is to restructure the code so that there is only one exit, using
if/else etc. and storing the result for the exit. Another (which can be
combined) is to use status flags and/or state variables.

Another way to do the restructuring is to wrap the multi-
return function inside a caller that handles the resource
allocation and cleanup:

void f(void) {
char *string = malloc(...);
FILE *stream = fopen(...);
inner_f_with_multiple_returns(string, stream);
fclose(stream);
free(string);
}

This requires that the "dirtying" can all be done before
the "real work," and that all the cleanup can be postponed
until afterward. However, that pattern describes quite a
few functions -- and those that it doesn't describe might
be candidates for decomposition anyhow.
 
C

Chris Croughton

Chris said:
[...]
One way is to restructure the code so that there is only one exit, using
if/else etc. and storing the result for the exit. Another (which can be
combined) is to use status flags and/or state variables.

Another way to do the restructuring is to wrap the multi-
return function inside a caller that handles the resource
allocation and cleanup:

Yes, if the resources are simple enough that's the way I do it. If it
involves passing more than a couple of extra parameters then I find it
messy (or if some of the allocation needs extra calculation first).
This requires that the "dirtying" can all be done before
the "real work," and that all the cleanup can be postponed
until afterward. However, that pattern describes quite a
few functions -- and those that it doesn't describe might
be candidates for decomposition anyhow.

As in the woman who asked "Is Beethoven still composing?" and got the
reply "No madam, he is decomposing!" <g>. I think some code should
definitely decomposed, or even composted...

Yes, most functions I've used which allocate and then free resources
have been able to be implemented using wrappers as you describe. A few
haven't, and some have been imossible to split up without making the
interfaces very odd and nonintuitive...

(Actually, I haven't used a goto in C for at least 10 years, I've always
been able to code round it. I've seen some code where it was the
"obviously correct" answer, though...)

Chris C
 
H

Herbert Rosenau

Hi,

when I need to execute a general clean-up procedure (inside of a
function) just before the function returns -- how do I do that when
there are several returns spread over the whole function?

There are different details:
- follow the roule that no fuction should be have more lines
of code than a screen (print) page can show.
This makes it easier to understund the code
and helps debugging the whole program
- if the function goes too big for the rule above you tries
to make too much details at once. Then break down the whole
job in multiple little functions (even as each function
gets called only one).
- define the style for each function:
- has only ONE return in case its job gets done well
but multiple returns on errors.
This will be the case when the awaited result is:
"done anything well"
- has only one return in case its job fails
but multiple returns its job is done well.
This will be the case when the natural result of that job
is: "something fails as expected"
- has only exactly ONE return point
This is the case when even something fails the total
work has to be done and either NO fail at all occurs
or even NO result is needed.
depending if you can identify WELL as ONE single value
or its easier to define ONE single value as error indicator.
- define the most significant value as return value and
less significant ones (when there are some)
as parameter.
The function result will be
- only an indicator of success/fail (like pointer/NULL pointer)
optionally another parameter gives more details about errors
- a pure error code
- one value indicates success (e.g. 0)
any other gives the error number
In both cases you needs on the calling point only one if
to differ between success and fail of this step
- Splitting out a whole switch statement
into a separate function using the roles above.
This helps you to differ WHAT from HOW.
- You may even have a need to split out each (or some)
case into own functions to separate out "WHAT is to do"
from "HOW is it done"
- in general:
when a function gets too big to follow the first role
it is easy too see that you have too much work for a
single function.
Spitting out "WHAT" from "HOW" is to do helps you
to get your mind clean. Define a function for each "What"
to hide details from the "WHAT".
If "HOW" is too much again split this detail again.
This method will allow you
- debug each detail alone by writing a debug main(),
compile little function alone, test it using different
testdata.
- debug outer functions using theyr input leaving out
inner functions during trace because you knows they
should work already.
- split up each function in at least 3 logical blocks
whereas each can be empty:
1. init
A check input parameters when needed
B allocate resources the fuction and its helper functions
needs - but only if A was successfull
a memory/files needed for internal work
b memory/files needed to return to caller
2. do the work only when 1. successfull
3. clean up
free all resources you have not return to the caller,
a close/remove files (only when they are created/opened
successfull).
Attention: the result from close() and rmove()
may change your result from success to fail!
b free memory (free() allows to be called with NULL
pointers! So you can blindly free() anything.
Each of 1-3, A+B, a+b can be done one or more helper fuctions.
if, if - else, if - else - if is a wounderful constunct - but
only if you gets your code so structured that they are NOT
got nested deep. Deep nesting points you to a design error.
Roll up the WHAT/HOW levels from scratch at this point.

At least your whole program will follow the rule:
I WHAT is to do.
Describes a (sub)job on what kind on work is to do
but lefts open HOW it gets done by calling II as function
for each step. When the WHAT is too complex the (sub)job
gets broken into a bit more detail of functions I.
This will describe your program in a logical way
without holding up with detail questions.
II HOW is it done
This does the real work in detail. In general you would
go recursively to I to get more description of WHAT
by working out mathematical formulas, formatting data
and so on. You'll break down to I again when during design
you sees more need to describe WHAT is to do and recursive
into II when the HOW is too complex to get done in a single
page.

So evakuating the body of nested or long wounded loops (for, while,
do) into functions of theyr own will remove lots of details from your
brain until you writes the details. You shortens switch statements to
"some different work based on this" is done. You shortens if and else
to "when this condition then this kind of work is to do" without
showing the details until you writes them.

This helps you to avoid lots of
- nested if and else if statements
- gotos (destroying the flow)
- higher nested for/while/do statements
in hiding details fom the flow until you needs them really.

I'm learned that techics in times whereas
- memory was only in amout of KB instead MB available
- assembly was the highest possible programming language
by
- thinking in objects builded of objects builded....
but nothing on help available than my brain to build them
(is there another method to get hudge programms in low memory?)
- need of reuse so many functions as possible
because having every time to less memory available
- having real time work on CPUs having not even 1 single MHz (instead
of xxGHz today).

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 
A

Albert van der Horst

Hi,

when I need to execute a general clean-up procedure (inside of a
function) just before the function returns -- how do I do that when
there are several returns spread over the whole function?

This points to one of the deficits in standard c.
I leave it to others to point to a way out, personally I
would probably use goto here.

The theoretical nice way to do this is, is coroutines, where
a facility and its clean ups can be tied together.
Nobody realizes this nowadays, 'cause when all you have is a hammer...
(I can't elaborate here, without going of topic.)
Thanks
Felix

Groetjes Albert.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top