General method for function return
Hi:
The following listed four types of function failure report
in my considering for comment.
1) Function result is passed by return
This shoud be the basic way of error report
class Ret { /*...*/ };
Ret f1(void);
Ret f2(void);
Ret f3(void);
Ret f1()
{
Ret r;
if((r=f2())!=OK) {
return(r);
}
if((r=f3())==ERR1) {
return Ret(ERR2);
}
}
return(OK);
};
If somethig left to be desired may be the repeated testing 'looked'
informationless.
2) Placing the return object at the first argument
The motivation is to be faster and smaller code size, since Ret is
only constructed once in the call chain, other uses are by
reference.
void f1(Ret&);
void f2(Ret&);
void f3(Ret&);
void f1(Ret& r)
{
f2(r);
if(r!=OK) {
return;
}
f3(r);
if(r==ERR1) {
r=ERR2;
return;
}
r=OK;
};
3) C++ native throw mechanism
Since in the stack unwind, nothing prevents an asynchronous throw
from somewhere, the throw should consider an unexpected
termination. Another fundamental difficulty is for the caller not
to rethrow, except few.
void f1(void); // may throw
void f2(void); // may throw
void f3(void); // may throw
void f1(void) {
try { f2(); }
catch(...) {
// cleanup to rethrow
throw;
};
try { f3(); }
catch(...) {
// cleanup to rethrow
throw;
};
};
Since many objects are made RAII, the mostly written code would be
void f1(void)
{
f2();
f3();
};
4) To improve the absorbing problem of 3)
Be there a thorw type so documented and used that caller can absorb.
class Absorbable { /*...*/ };
void f1(void)
{
try { f2(); }
cathc(const Absorbable& e) {
// absort the failure report
// FALLTHROUGH
};
try { f3(); }
cathc(const Absorbable& e) {
// translate Absorbable, particularly if e is not suitable for
// f1() to report
throw Absorbable( /*different contents*/ );
// Or, if f1() report failure by return, convert to a meanful
// return objet
};
f4(); // f1 has to document to throw Absorbable
// to propagate the same Absorbable from f4(), which is
// also documented to throw Absorbable. Or let some
// mechanism make the thrown object different, so won't
// be caught by 'catch(const Absorbable&)' further.
};
It may seem not easy to conclude which one (or others not listed)
is entirely preferrable. Something I do consider are:
a) Constructor and operator overloads should throw in failure.
b) Caller function should be able to absorb the failure report.
Then method 3) is ruled out because of b) and that Method 4) can
be adopted instead.
Method 4) is worse than method 1) because of the unexpected
termination concern, but constructors need to use the throw
method, so normal functions should use method 1) or 2).
Any comment?
IJ. Wang
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]