Re: C++0x/1x exception specifications proposal: Compile-time checked
Ioannis Vranos wrote:
My current direction of thought in compile-time exception specifications:
The compile-time exception specifications of each function and member
function, are about declaring any *additional* exception the specific
function or member function will throw, and not about redeclaring the
"inherited" exceptions.
That is, each function/member function is a level, and each level
declares two things: The exceptions it can throw by itself using the
_throw keyword, and the "inherited"exceptions it can handle, that are
denoted with the _nothrow keyword.
At compile-time, those exception specifications are *accumulated*, and
at the caller level we specify (the caller function or member function),
we get a compile-time result of the exceptions that it can receive.
That is:
template <class T>
void somefunc(T &a) _throw()
{
// ...
}
Here somefunc() indicates that itself will not throw any additional
exceptions. One of its arguments may throw one, but itself will throw no
exception. That is, the compile-time exception specification of this
template function is correct.
At compile-time, those exception-specifications will be *accumulated* up
to the desired level of exception handling, where we will know the exact
types of exceptions we can handle.
Another example with a template:
template <class T>
void somefunc(T &a) _throw()
{
// ...
} _nothrow(std::bad_alloc)
This indicates that somefunc() will not throw any exceptions itself,
while it also handles the case of std::bad_alloc. This means
std::bad_alloc will stop being *accumulated* at the compile-time
exception checking.
==> So this is what it remains to be resolved:
How can we retrieve the accumulated exception types information, at a
desired level where we want to handle them, with compile-time messages?
Example:
void somefunc2() _throw(graph_exception)
{
// ...
}
void somefunc1() _throw(time_exception)
{
somefunc2();
} _nothrow(std::out_of_range)
// The question that remains to be solved is: How can we find out at the
// point of somefunc() call in main(), that we can receive
// time_exception and graph_exception, whose info has been *accumulated*
// at compile-time by the use of _throw/_nothrow compile-time
// specifications, so we can handle them there?
int main() try
{
somefunc1();
}
catch(time_exception)
{
// ...
}
catch(graph_exception)
{
// ...
}
The solution that I propose on this, is the keyword :exceptions or
:_exceptions:
void somefunc2() _throw(graph_exception)
{
throw graph_exception();
}
void somefunc1() _throw(time_exception)
{
somefunc2();
throw time_exception();
} _nothrow(std::out_of_range)
int main()
{
somefunc1() :exceptions;
}
at compile-time it will produce a compiler message, something like:
"main()::somefunc1() may throw exceptions:
somefunc1()::somefunc2()::graph_exception, somefunc1::time_exception".
After we write our exception handlers, we can remove the keyword
":exceptions" from somefunc1(); statement in main(), and thus main becomes:
void somefunc2() _throw(graph_exception)
{
throw graph_exception();
}
void somefunc1() _throw(time_exception)
{
somefunc2();
throw time_exception();
} _nothrow(std::out_of_range)
int main() try
{
somefunc1();
}
catch(graph_exception)
{
// ...
}
catch(time_exception)
{
// ...
}