Re: Exception handling the right way
In article <48c77383$0$197$e4fe514c@news.xs4all.nl>,
Bart Friederichs <bf@tbwb.nl> wrote:
Yannick Tremblay wrote:
Don't get me wrong, I really respect the writings of Alexandrescu as
well he has brought to the C++ community. However, I have serious
doubts about recommending his writing to a beginner that is trying to
figure out exceptions. I fear that he would get lost in the
complexity of the details and templates presented in this article (or
in Loki and Mordern C++).
I am not a beginner. I understand the scopeguard completely. I myself
though have serious doubts about the usability of it in our applications
(time critical automation control). I get a slight sense of extra
clutter in my code.
Sorry, I didn't mean to hurt your sensibility. My view is that this
article goes through the technical details on how to implement a
generic multi-purpose scope guard. The details and the complexity of
the template syntax and coping with variable number of arguments
makes it not a good teaching tool on what is a scope guard, why is it
useful and how it simplify coding and improve code quality. (It is
however a very useful template to have in one's utility collection.)
The concept can be understodd much more clearly from say:
class AutoFileCloser
{
public:
SpecificFileCloser(FILE * file): m_file(file) {};
~SpecificFileCloser()
{
if(m_file) fclose(m_file);
}
private:
FILE * m_file;
};
void bar1(FILE const * file); // defined somewhere
void bar2();
std::vector<int> bar3(FILE const * file, int i);
void foo()
{
FILE * file = fopen("somepath");
AutoFileCloser fileCloser(file);
// Do things that can throw exceptions without hassle
// e.g.
bar1(file);
bar2();
std::vector<int> v = bar3(file, 42);
return;
}
versus the non expection using version:
typedef int ERROR_TYPE;
#define SUCCESS 0;
#define FAILURE1 -1
#define FAILURE2 -2
#define FAILURE3 -3
ERROR_TYPE bar1(FILE * file);
ERROR_TYPE bar2();
ERROR_TYPE bar3(FILE * file, int i, std::vector<int> & vOut);
ERROR_TYPE foo()
{
FILE * file = fopen("somepath");
if(SUCCESS != bar1(file))
{
fclose(file);
return FAILURE1;
}
if(SUCCESS != bar2())
{
fclose(file);
return FAILURE2;
}
std::vector<int> v;
if(SUCCESS != bar3(file, 42, v))
{
fclose(file);
return FAILURE3;
}
return SUCCESS;
}
To me, there is no question that the first version of foo() clearly
shows what it does with very little noise stopping me from seeing the
intended behaviour. The second version has more failure handling code
in it than actual intention. So to understand it, I constantly have
to context switch between error handling mode and intended action
mode. I much prefer doing the two separately as IMO, it is simpler
and easier to get right. Note that the second foo() also hides a
bug that is typically seen in code of "C programmers that moved to C++
but fear exceptions so don't use them but also ignore them"
In a basic example as above, one can concentrate on foo() which is
what is important. In the Alexandrescu article, one is likely to
concentrate on the complexity of implementing the generic ScopeGuard
(which you do only once) rather than its usefulness and might very
well generates feelings of: "this is much too complicated, I'll just
not use exceptions."
I just never seriously used exception handling, because I never needed
it. I want to figure out now, if I *really* don't need it, or if I am
missing on some useful programming concepts.
If you have used C++ with new (rather than new(nothrow) and the STL
then you may very well have written bad code. That's the subtle bug I
am talking about above. The line that define the vector may throw an
exception. bar3(...) probably push elements on the vector and as it
is written with return code in mind by someone who "doesn't use
exceptions" there's a real risk that it doesn't catch any exceptions
that can be generated by STL operations. If an exception is thrown in
either of these places, fclose(file) will never be called and you will
leak resources.
IMO, there are only two valid ways to write the barX() functions:
either they do not return any error status and use exceptions for all
error reporting or they return an error status **and** the entire body
of the function is a try/catch block so that no function that return
error status can ever throw exceptions. In the later case, you will
correctly avoid using exceptions in your code but you will forever be
shackled with hundreds of fuction level try/catch blocks and keep
hating excpetions and swear at the idiots that added them to the C++
language. you may as well give up, embrace exceptions and give up on
returning error status instead. In the long term, it will make your
life easier and your code better.
So personally, I'd say yes, you are missing on some very useful
programming concept. More critically, you are potentially writing
buggy code.
In that regard, I would always recommend to read S. Meyer first, then
H. Sutter and once you grasp both of them, you can move to
Alexandrescu. I would never recommend to read them in the opposite
order.
Do you have any links to that S. Meyer and H. Sutter?
They are well known authors.
Buy the books, they are worth it:
http://www.aristeia.com/ Scott Meyers' page with links to his main books.
(http://www.aristeia.com/books.html)
http://www.gotw.ca/publications/index.htm Herb Sutter publications
page. Some of the content of his books are based on articles avaialbe
on his Guru of the Week web page.
Also you might want to start by reading through the C++ FAQ lite:
http://www.parashift.com/c++-faq-lite/
Yannick