throwing exceptions from constructor and other alternatives
Hi,
I stucked on how to desing a certain class. The only part where the
algorithm can fail is at the initialization point. My question is how
to handle exceptions that can only happen at initialization. c++ throw
from constructors seem not to be optimal from the point of view of the
resulting syntax.
To give a concrete example: The class is a simple root finder, it is
usage is like this:
class function f; //defined somewhere else
double lb=1; //lower bound for function root
double ud=3; //upper bound for function root
root_finder rf(f, lb, ud); //constructor
while(root_finder.error()>0.001){
root_finder.iterate(); //iterates by bisecting the known bracket
for the root
}
cout<<"root is "<<root_finder.estimate()<<endl;
the implementation is a simple bisection algorithm, therefore it is
guarantied to converge.
The only problem that can happen is that the initial guess for lower
bound and upper bound don't bracket an odd number of roots. Once the
initial bracket encloses at least one root the "iterate()" method
doesn't have to check explicitly that the current bracket is valid. So,
in my logic, "exception only can occur during the initialization".
So trying to be elegant I added a throw exception into the root_finder
constructor: when f(lb)*f(up)>0 [i.e. both are same sign] the
constructor throws an exception
class root_finder{
root_finder(function const& f; double lb; double ub){
...
flb=f(lb); //I need to calculated this anyway for initializing
fub=f(ub); // the bracketing algorithm
if( flb*fub > 0) throw root_finder::bad_bracket; // exception.
this is the ONLY bad thing that can ever happen
...
}
}
But resulted in a bad idea because when I enclose the constructor in a
try/catch block I have to include the iterations as well. That means
that I can not do something like this;
try{
root_finder rf(f, lb, ub);
} catch(bad_bracket){
root_finder rf(f, lb-1.0, ub+1.0); //increases the bracket size;
}
while(rf.error()>0.0001){ //<- error: rf is not in this scope;
rf.iterate();
}
//the only way around is to duplicate the while block in each try/catch
block. Duplicating the while block seem to be an awful consequence of
(the elegant?) throwing exception from the constructor.
So, there are three alternatives I can think of:
1) construct a blank rf [that defers the initialization]
root_finder rf;
try{
rf=root_finder(f, lb, ub);
} catch (...){
rf=root_finder(f, lb-1.0, ub+1.0);
}
...
I don't like this solution because it requieres to define a
public default constructor root_finder() which can bring more problems
2) switch to pointer syntax
root_finder rfP=0;
try{
rfP=new root_finder(f, lb, ub);
}catch(...){
rfP=new root_finder(f, lb-1.0, ub+1.0);
}
while(rfP->error()>0.01){ rfP->iterate();}
...
I don't like this solution because it uses pointers.
3) don't use exceptions at all and give root_finder class a flag
status (like std::istream have)
root_finder rf(f, lb, ub);
if(!rf) rf=root_finder(f, lb-1.0, ub+1.0);
assert(rf);
while(rf.error()>0.01){rf.iterate();}
...
I don't like this solution because I don't like flags and that
flag is only useful just after construction. BUT it may be the best
solution anyways unless some gives me some alternative.
So,
Do you have any opinion which is the most common approach for exception
errors that can only happen at construction/initialization?
Do you have any other solutions?
Is this kind of exception forcing us to separate construction from
initialization (wathever this means) even in simple classes like this
one?
Thank you, Alfredo
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]