Hello!
Please advise on the following issue. I have a function which returns
1 or 0 in multiple branches, and it must perform the same code before
each return, like this:
int fun()
{
if (some code)
{
restore(param1,param2,param3,param4,param5);
return 1;
}
else if (some code)
{
restore(param1,param2,param3,param4,param5);
return 0;
}
restore(param1,param2,param3,param4,param5);
return 1;
}
Is there any alternative way to execute some code upon return from the
function in pure C? Repeated code makes my function more than twice
longer and it losts its readability. The only thing coming to my mind
is goto.
Thanks, Alex.
Good question. This is one situation that inspired the development (a
long time ago) of exception mechanisms. To generalize your example a
little, many functions that honor unix return code standards are
structured like this...
int fun(...)
{
...
if (<condition1>) {
<cleanup code>
return 1;
}
< normal processing 1 >
...
if (<condition2>) {
<cleanup code>
return 2;
}
...
< normal processing 2>
... yada yada ...
return 0;
}
This is really annoying when (as usual) the cleanup code needs access
to many local variables. In a block structured languate with nested
functions, you would define a cleanup function with access to those
variables. In pseudo-C,
int fun(...)
{
<local var decls>
int cleanup(int return_code)
{
<cleanup code>
return return_code;
}
...
if (<condition1>) return cleanup(1);
< normal processing 1 >
...
if (<condition2>) return cleanup(2);
...
< normal processing 2>
... yada yada ...
return 0;
}
Since C doesn't have nested functions, one way to go is "fake" them
with macros. This strategy is far from ideal, but it does have a
rationale. It avoids the problems of goto's and introducing a very
long-lived "ret_val" variable, which has maintenance problems.
int fun(...)
{
<local var decls>
#define CLEANUP_AND_RETURN(N) do { \
<cleanup code> \
return (N); \
} while (0);
...
if (<condition1>) CLEANUP_AND_RETURN(1);
< normal processing 1 >
...
if (<condition2>) CLEANUP_AND_RETURN(2);
...
< normal processing 2>
... yada yada ...
return 0;
#undef CLEANUP_AND_RETURN
}
Another way to go is bottle up all the local variables needed for
cleanup in a struct so that a non-nested cleanup function can do its
work.
struct foo_vars_s { <local var decls };
int cleanup(int ret_val, struct foo_vars_s *o)
{
<cleanup code accessing variable x as o->x >
return ret_val;
}
int fun()
{
struct foo_vars_s o[1];
< local var ref x is replaced by o->x below >
...
if (<condition1>) return cleanup(1, o);
< normal processing 1 >
...
if (<condition2>) return cleanup(2, o);
...
< normal processing 2>
... yada yada ...
return 0;
}
This isn't ideal either.