Exception handling. Extension request
Hello!
In current C++ language exception handling is defined with well
structured exception hierarchies in mind. In this case exception
handling is simple: catch(base_exception& e) and be happy.
Problem arises when one have to handle many unrelated exceptions
that does not correspond to single hierarchy or even in a not so rare
case where one needs to provide different responses for different
exceptions in the same hierarchy.
The first is common on subsystem boundaries like C++ - C or C++ - GUI
event handlers. The second is common in system-level errors handling
as it is in boost::filesystem library.
There are workarounds exists that are based on "throw in catch(...)"
idiom:
try {
//...
} catch(...) {
handle_exceptions();
}
void handle_exceptions() {
assert(std::uncaught_exception());
try {
throw;
} catch (exception_type1& e) {
//handle it
} catch (exception_type2& e) {
//handle it
} catch (exception_type3& e) {
//handle it
}
// etc.
}
But I believe that the problem mentioned can be elegantly solved on
a language level. All we need is just allowing the following:
class A {};
class B {
public :
B(A const& a);
};
void foo()
try {
throw A;
} catch (B b) {
//caught
}
The idea behind it is simple: if A was thrown and B can be constructed
from it then the catch(B b) handler is called.
With this extension one can do the following:
typedef boost::variant< int, std::string > my_exception;
class my_exception_visitor
: public boost::static_visitor<>
{
public:
void operator()(int & i) const
{
//...
}
void operator()(std::string & str) const
{
//...
}
};
void foo()
try {
if(...) throw 1;
else throw std::string("Hello world!");
} catch (my_exception e) {
boost::apply_visitor( my_exception_visitor(), e );
}
In order to use polymorphic exceptions one can do:
#include <iostream>
#include <boost/variant.hpp>
class base {
public :
virtual void print() const {
std::cout << "base\n";
}
};
class child :
public base {
public :
void print() const {
std::cout << "child\n";
}
};
class my_visitor :
public boost::static_visitor<>
{
public:
void operator()(base& b) const
{
std::cout << "visit base\n";
b.print();
}
// it is not called
void operator()(child& c) const
{
std::cout << "visit child\n";
c.print();
}
void operator()(int i) const
{
std::cout << "int\n";
}
};
#include <boost/ref.hpp>
int main()
{
child c;
typedef boost::variant<boost::reference_wrapper<base>, int> var_t;
// boost::reference_wrapper should be patched to allow the next
line to compile
// the following constructor should be added to the
boost::reference_wrapper
class:
// template<typename T1>
// reference_wrapper(reference_wrapper<T1> const& rw):
t_(rw.get_pointer()) {}
var_t v = boost::ref(c);
boost::apply_visitor( my_visitor(), v );
// prints:
// visit base
// child
base b;
v = boost::ref(b);
boost::apply_visitor( my_visitor(), v );
// prints:
// visit base
// base
std::cin.get();
return 0;
}
For boost::filesystem library it means that its clients can use uniform
"exception handling" even without exceptions at all. Versions of
functions with error_code could be replaced with templates that can
accept any visitor instead of error_code. It would be a high-level,
type-safe, reusable and maintainable way to handle system-level
errors in this particular case and in common case where
catch(base_exception&) is not enough.
This post is a call for comments/opinions/improvements of the
basic idea presented.
Best,
Oleg Abrosimov.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]