f



C++0x/1x exception specifications proposal: Compile-time checked

Perhaps a mechanism can be introduced in the C++0x/1x standard, 
something simple like defining a function as:


void somefunc(void) throw()
{
  // ...
}


and getting a compile time error saying something like:


"Error: void somefunc(void) throw(): Wrong exception specification. 
somefunc can throw std::bad_alloc, std::range_error".

That is make the compiler to check exception specifications for errors too.



Ioannis A. Vranos
0
ivranos1 (46)
1/20/2008 5:13:04 PM
comp.lang.c++ 49423 articles. 7 followers. Post Follow

22 Replies
501 Views

Similar Articles

[PageSpeed] 6

In article <fmvvf1$1i2i$1@ulysses.noc.ntua.gr>, 
ivranos@no.spamfreemail.nospam.gr says...
> Perhaps a mechanism can be introduced in the C++0x/1x standard, 
> something simple like defining a function as:
> 
> void somefunc(void) throw()

[ ... ]

> and getting a compile time error saying something like:
> 
> "Error: void somefunc(void) throw(): Wrong exception specification. 
> somefunc can throw std::bad_alloc, std::range_error".
> 
> That is make the compiler to check exception specifications for errors too.

This is next to impossible in practice. In particular, the compiler 
doesn't have anything to check against with externally defined functions 
-- i.e. you include a header with an exception specification, but the 
compiler has no access to the implementation of the "stuff" in that 
header (unless it happens to be included inline).

In practice, doing so probably wouldn't be a good idea anyway. Java has 
checked exceptions, where the compiler does (more or less) what you're 
advocating. Experience has shown (repeatedly) that while this seems like 
a good idea, it almost never works well in practice.

In the end, exception specifications are an anti-pattern -- something 
that initially seems like a good idea, but in the long run ends up 
causing far more problems than it solves.

One of the basic ideas of exception handling is that it sets up more or 
less a "tunnel" from the place an exception is thrown to the place it is 
caught, and any intermediate layers need not worry about it (beyond 
being exception safe). Exception specifications (especially enforced as 
you're advocating) require that all intermediate levels DO pay attention 
to the exceptions that propagate from the lower levels, though the 
intermediate layer has no real interaction with the exception itself at 
all. Worse, an intermediate layer might easily work with a function via 
a pointer, in which case it simply has no way of knowing what exceptions 
might be thrown through it by that function. You could make the 
exception specification part of the function signature, so it could only 
take pointers to functions that threw from a limited selection of 
exceptions, but this would frequently be quite limiting. In many cases, 
such call-back functions (and such) haven't even been contemplated when 
the code that uses them is written, so it's essentially impossible for 
them to even guess at what exceptions they might throw.

In addition, templates and exception specifications are essentially 
impossible to blend. A typical template can throw most (if not all) the 
exceptions that can be thrown by the type over which it is instantiated 
-- but an many cases, nobody has yet imagined the type over which it 
will be isntantiated when the template itself is written.

I'll repeat: exception specifications are an anti-pattern. They're 
nearly always bad now, and having the compiler attempt to enforce them 
would make them even worse.

-- 
    Later,
    Jerry.

The universe is a figment of its own imagination.
0
jcoffin (2241)
1/20/2008 6:49:36 PM
On 2008-01-20 18:13, Ioannis Vranos wrote:
> Perhaps a mechanism can be introduced in the C++0x/1x standard, 
> something simple like defining a function as:
> 
> 
> void somefunc(void) throw()
> {
>   // ...
> }
> 
> 
> and getting a compile time error saying something like:
> 
> 
> "Error: void somefunc(void) throw(): Wrong exception specification. 
> somefunc can throw std::bad_alloc, std::range_error".
> 
> That is make the compiler to check exception specifications for errors too.

You might be interested in the following two articles (just a few among
many discussing the subject):

http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!149.entry
http://www.mindview.net/Etc/Discussions/CheckedExceptions

-- 
Erik Wikström
0
Erik-wikstrom (1881)
1/20/2008 7:52:31 PM
Ioannis Vranos wrote:
> Perhaps a mechanism can be introduced in the C++0x/1x standard, 
> something simple like defining a function as:
> 
> 
> void somefunc(void) throw()
> {
>  // ...
> }
> 
> 
> and getting a compile time error saying something like:
> 
> 
> "Error: void somefunc(void) throw(): Wrong exception specification. 
> somefunc can throw std::bad_alloc, std::range_error".
> 
> That is make the compiler to check exception specifications for errors too.


Some more details:


What I have in mind is "loose" application. That is, the compiler should 
check only the available code accessible to it.


In addition, I think throw(void) should be equivalent to throw(), and 
throw(...) should be equivalent to non-throw specification, e.g.:


// May throw anything.
void somefunc()
{
  // ...
}


// May throw anything.
void some func() throw(...)
{
  // ...
}


I think that even loose, the compile time checking of throw 
specifications will improve the overall exception safety, and everyone 
will usually know the exact types of exceptions he can catch on a given 
call.


Any existing third-party, pre-built C++ library can be revised to 
provide exception specifications at its next release. That is, in the 
future the total exception safety will increase, because the exception 
specification mechanism will finally work (since now I think it doesn't 
work as it is provided, in practice).

Or that library can keep providing its facilities without exception 
specifications or with the throw(...) equivalent ones.


If I may repeat, what I have in mind is "loose" application. That is, 
the compiler should check only the available code accessible to it.


In the case of some function having the throw() specification, is using 
indirectly code that may throw some exception, it can be flagged as a 
compile-time error, provided that this source code that is used 
indirectly by this function, is accessible to the compiler. Otherwise, 
it will not be flagged as a compile-time error.

The run-time stuff of the throw specifications will still apply.
0
ivranos1 (46)
1/20/2008 7:58:31 PM
Ioannis Vranos wrote:
> Ioannis Vranos wrote:
>> Perhaps a mechanism can be introduced in the C++0x/1x standard, 
>> something simple like defining a function as:
>>
>>
>> void somefunc(void) throw()
>> {
>>  // ...
>> }
>>
>>
>> and getting a compile time error saying something like:
>>
>>
>> "Error: void somefunc(void) throw(): Wrong exception specification. 
>> somefunc can throw std::bad_alloc, std::range_error".
>>
>> That is make the compiler to check exception specifications for errors 
>> too.
> 
> 
> Some more details:
> 
> 
> What I have in mind is "loose" application. That is, the compiler should 
> check only the available code accessible to it.
> 
> 
> In addition, I think throw(void) should be equivalent to throw(), and 
> throw(...) should be equivalent to non-throw specification, e.g.:
> 
> 
> // May throw anything.
> void somefunc()
> {
>  // ...
> }
> 
> 
> // May throw anything.
> void some func() throw(...)
> {
>  // ...
> }
> 
> 
> I think that even loose, the compile time checking of throw 
> specifications will improve the overall exception safety, and everyone 
> will usually know the exact types of exceptions he can catch on a given 
> call.
> 
> 
> Any existing third-party, pre-built C++ library can be revised to 
> provide exception specifications at its next release. That is, in the 
> future the total exception safety will increase, because the exception 
> specification mechanism will finally work (since now I think it doesn't 
> work as it is provided, in practice).
> 
> Or that library can keep providing its facilities without exception 
> specifications or with the throw(...) equivalent ones.
> 
> 
> If I may repeat, what I have in mind is "loose" application. That is, 
> the compiler should check only the available code accessible to it.
> 
> 
> In the case of some function having the throw() specification, is using 
> indirectly code that may throw some exception, it can be flagged as a 
> compile-time error, provided that this source code that is used 
> indirectly by this function, is accessible to the compiler. Otherwise, 
> it will not be flagged as a compile-time error.
> 
> The run-time stuff of the throw specifications will still apply.


Addition 2:

In this proposal, I think an opposite of the throw keyword may be 
needed, to specify the exceptions that the compiler will ignore. I think 
the right place of it, is the end of the function scope. An example:


void somefunc() throw()
{
   std::vector<int> vec;

  // ...

} nothrow(std::out_of_range)

0
ivranos1 (46)
1/20/2008 9:13:42 PM
Ioannis Vranos wrote:
> Ioannis Vranos wrote:
>> Ioannis Vranos wrote:
>>> Perhaps a mechanism can be introduced in the C++0x/1x standard, 
>>> something simple like defining a function as:
>>>
>>>
>>> void somefunc(void) throw()
>>> {
>>>  // ...
>>> }
>>>
>>>
>>> and getting a compile time error saying something like:
>>>
>>>
>>> "Error: void somefunc(void) throw(): Wrong exception specification. 
>>> somefunc can throw std::bad_alloc, std::range_error".
>>>
>>> That is make the compiler to check exception specifications for 
>>> errors too.
>>
>>
>> Some more details:
>>
>>
>> What I have in mind is "loose" application. That is, the compiler 
>> should check only the available code accessible to it.
>>
>>
>> In addition, I think throw(void) should be equivalent to throw(), and 
>> throw(...) should be equivalent to non-throw specification, e.g.:
>>
>>
>> // May throw anything.
>> void somefunc()
>> {
>>  // ...
>> }
>>
>>
>> // May throw anything.
>> void some func() throw(...)
>> {
>>  // ...
>> }
>>
>>
>> I think that even loose, the compile time checking of throw 
>> specifications will improve the overall exception safety, and everyone 
>> will usually know the exact types of exceptions he can catch on a 
>> given call.
>>
>>
>> Any existing third-party, pre-built C++ library can be revised to 
>> provide exception specifications at its next release. That is, in the 
>> future the total exception safety will increase, because the exception 
>> specification mechanism will finally work (since now I think it 
>> doesn't work as it is provided, in practice).
>>
>> Or that library can keep providing its facilities without exception 
>> specifications or with the throw(...) equivalent ones.
>>
>>
>> If I may repeat, what I have in mind is "loose" application. That is, 
>> the compiler should check only the available code accessible to it.
>>
>>
>> In the case of some function having the throw() specification, is 
>> using indirectly code that may throw some exception, it can be flagged 
>> as a compile-time error, provided that this source code that is used 
>> indirectly by this function, is accessible to the compiler. Otherwise, 
>> it will not be flagged as a compile-time error.
>>
>> The run-time stuff of the throw specifications will still apply.
> 
> 
> Addition 2:
> 
> In this proposal, I think an opposite of the throw keyword may be 
> needed, to specify the exceptions that the compiler will ignore. I think 
> the right place of it, is the end of the function scope. An example:
> 
> 
> void somefunc() throw()
> {
>   std::vector<int> vec;
> 
>  // ...
> 
> } nothrow(std::out_of_range)



Some examples of these:


I.

   void somefunc() throw(std::bad_alloc, my_app::graph_range_error)
   {
     // ...
   } nothrow(std::out_of_range)



II. Equivalent cases:

   void somefunc() throw()
   {
     // ...
   } nothrow(std::bad_alloc)


   void somefunc() throw(void)
   {
     // ...
   } nothrow(std::bad_alloc)



III. Equivalent cases:

   void somefunc()
   {
     // ...
   }

   void somefunc() throw(...)
   {
     // ...
   }

   void somefunc() throw(...)
   {
     // ...
   } nothrow()


   void somefunc()
   {
     //...
   } nothrow()


   void somefunc() throw(...)
   {
     // ...
   } nothrow(void)


   void somefunc()
   {
     //...
   } nothrow(void)


0
ivranos1 (46)
1/20/2008 10:46:28 PM
Ioannis Vranos wrote:
> 
>>>> Perhaps a mechanism can be introduced in the C++0x/1x standard, 
>>>> something simple like defining a function as:
>>>>
>>>>
>>>> void somefunc(void) throw()
>>>> {
>>>>  // ...
>>>> }
>>>>
>>>>
>>>> and getting a compile time error saying something like:
>>>>
>>>>
>>>> "Error: void somefunc(void) throw(): Wrong exception specification. 
>>>> somefunc can throw std::bad_alloc, std::range_error".
>>>>
>>>> That is make the compiler to check exception specifications for 
>>>> errors too.
>>>
>>>
>>> Some more details:
>>>
>>>
>>> What I have in mind is "loose" application. That is, the compiler 
>>> should check only the available code accessible to it.
>>>
>>>
>>> In addition, I think throw(void) should be equivalent to throw(), and 
>>> throw(...) should be equivalent to non-throw specification, e.g.:
>>>
>>>
>>> // May throw anything.
>>> void somefunc()
>>> {
>>>  // ...
>>> }
>>>
>>>
>>> // May throw anything.
>>> void some func() throw(...)
>>> {
>>>  // ...
>>> }
>>>
>>>
>>> I think that even loose, the compile time checking of throw 
>>> specifications will improve the overall exception safety, and 
>>> everyone will usually know the exact types of exceptions he can catch 
>>> on a given call.
>>>
>>>
>>> Any existing third-party, pre-built C++ library can be revised to 
>>> provide exception specifications at its next release. That is, in the 
>>> future the total exception safety will increase, because the 
>>> exception specification mechanism will finally work (since now I 
>>> think it doesn't work as it is provided, in practice).
>>>
>>> Or that library can keep providing its facilities without exception 
>>> specifications or with the throw(...) equivalent ones.
>>>
>>>
>>> If I may repeat, what I have in mind is "loose" application. That is, 
>>> the compiler should check only the available code accessible to it.
>>>
>>>
>>> In the case of some function having the throw() specification, is 
>>> using indirectly code that may throw some exception, it can be 
>>> flagged as a compile-time error, provided that this source code that 
>>> is used indirectly by this function, is accessible to the compiler. 
>>> Otherwise, it will not be flagged as a compile-time error.
>>>
>>> The run-time stuff of the throw specifications will still apply.
>>
>>
>> Addition 2:
>>
>> In this proposal, I think an opposite of the throw keyword may be 
>> needed, to specify the exceptions that the compiler will ignore. I 
>> think the right place of it, is the end of the function scope. An 
>> example:
>>
>>
>> void somefunc() throw()
>> {
>>   std::vector<int> vec;
>>
>>  // ...
>>
>> } nothrow(std::out_of_range)
> 
> 
> 
> Some examples of these:
> 
> 
> I.
> 
>   void somefunc() throw(std::bad_alloc, my_app::graph_range_error)
>   {
>     // ...
>   } nothrow(std::out_of_range)
> 
> 
> 
> II. Equivalent cases:
> 
>   void somefunc() throw()
>   {
>     // ...
>   } nothrow(std::bad_alloc)
> 
> 
>   void somefunc() throw(void)
>   {
>     // ...
>   } nothrow(std::bad_alloc)
> 
> 
> 
> III. Equivalent cases:
> 
>   void somefunc()
>   {
>     // ...
>   }
> 
>   void somefunc() throw(...)
>   {
>     // ...
>   }
> 
>   void somefunc() throw(...)
>   {
>     // ...
>   } nothrow()
> 
> 
>   void somefunc()
>   {
>     //...
>   } nothrow()
> 
> 
>   void somefunc() throw(...)
>   {
>     // ...
>   } nothrow(void)
> 
> 
>   void somefunc()
>   {
>     //...
>   } nothrow(void)


Addition 3:


IV.

// The compiler ignores all exceptions and considers that somefunc()
// will not throw any exception. Thus it will not provide a compile-time
// error.

  void somefunc() throw()
  {
      throw std::bad_alloc();
  } nothrow(...)
0
ivranos1 (46)
1/21/2008 11:29:37 AM
"Ioannis Vranos" <ivranos@no.spamfreemail.nospam.gr> wrote in message 
news:fmvvf1$1i2i$1@ulysses.noc.ntua.gr...

> Perhaps a mechanism can be introduced in the C++0x/1x standard, something 
> simple like defining a function as:

> void somefunc(void) throw()
> {
>  // ...
> }

> and getting a compile time error saying something like:

> "Error: void somefunc(void) throw(): Wrong exception specification. 
> somefunc can throw std::bad_alloc, std::range_error".

The problem with this is that *any* expression that has the capability of 
causing undefined behavior has the capability of throwing any exception.  In 
particular, this property means that an implementation is permitted to 
extend the language so that arithmetic errors, such as division by zero, 
throw exceptions.

What would you have such an implementation do about an arithmetic operation 
inside a throw() function that might overflow?  If the compiler complains 
about it, it is rejecting a program that might have nothing wrong with it. 
If it doesn't, then you need to figure out how to change your requirement to 
permit such behavior.


0
ark1 (690)
1/21/2008 3:12:57 PM
On 2008-01-20 23:46, Ioannis Vranos wrote:
> Ioannis Vranos wrote:

[snip]

Could you please choose one thread for this discussion? Posting the same
thing in multiple threads makes the discussion hard to follow.  And it
is a waste of bits :-)

-- 
Erik Wikström
0
Erik-wikstrom (1881)
1/21/2008 5:43:12 PM
Erik Wikström wrote:
> On 2008-01-20 23:46, Ioannis Vranos wrote:
>> Ioannis Vranos wrote:
> 
> [snip]
> 
> Could you please choose one thread for this discussion? Posting the same
> thing in multiple threads makes the discussion hard to follow.  And it
> is a waste of bits :-)


In my newsgroup reader (Thunderbird) the message appears in the same 
discussion thread, with subject "C++0x/1x exception specifications 
proposal: Compile-time checked".

0
ivranos1 (46)
1/21/2008 8:00:21 PM
On 2008-01-21 21:00, Ioannis Vranos wrote:
> Erik Wikström wrote:
>> On 2008-01-20 23:46, Ioannis Vranos wrote:
>>> Ioannis Vranos wrote:
>> 
>> [snip]
>> 
>> Could you please choose one thread for this discussion? Posting the same
>> thing in multiple threads makes the discussion hard to follow.  And it
>> is a waste of bits :-)
> 
> 
> In my newsgroup reader (Thunderbird) the message appears in the same 
> discussion thread, with subject "C++0x/1x exception specifications 
> proposal: Compile-time checked".

In my they do not, the messages are posted both in the thread "Bjarne's
comments about exception specification" and "C++0x/1x exception
specifications proposal: Compile-time checked". Google groups also see
two threads [1]. I notice that you are running Thunderbird 1.5, might be
some bug fixed in later versions.

1.
http://groups.google.com/group/comp.lang.c++/browse_frm/thread/7076130bcbce6038/9cb2f509b3527039?lnk=gst&q=bjarne%27s+comments#9cb2f509b3527039
http://groups.google.com/group/comp.lang.c++/browse_frm/thread/c96c5d2ecdcd7a0f/35b5716c348bae12?lnk=gst&q=C%2B%2B0x%2F1x+exception+specifications+proposal%3A+Compile-time+checked#35b5716c348bae12

-- 
Erik Wikström
0
Erik-wikstrom (1881)
1/21/2008 8:18:20 PM
Erik Wikström wrote:
>
>> In my newsgroup reader (Thunderbird) the message appears in the same 
>> discussion thread, with subject "C++0x/1x exception specifications 
>> proposal: Compile-time checked".
> 
> In my they do not, the messages are posted both in the thread "Bjarne's
> comments about exception specification" and "C++0x/1x exception
> specifications proposal: Compile-time checked". Google groups also see
> two threads [1]. I notice that you are running Thunderbird 1.5, might be
> some bug fixed in later versions.
> 
> 1.
> http://groups.google.com/group/comp.lang.c++/browse_frm/thread/7076130bcbce6038/9cb2f509b3527039?lnk=gst&q=bjarne%27s+comments#9cb2f509b3527039
> http://groups.google.com/group/comp.lang.c++/browse_frm/thread/c96c5d2ecdcd7a0f/35b5716c348bae12?lnk=gst&q=C%2B%2B0x%2F1x+exception+specifications+proposal%3A+Compile-time+checked#35b5716c348bae12


No, you are right on this. I indeed opened a new specific discussion 
thread about my proposal so as to gather more feedback.

0
ivranos1 (46)
1/21/2008 8:27:29 PM
A revised version of my proposal (version 1.9):


Design ideals:

1. *Loose* compile-time checking of throw compile-time specifications. 
The compiler reports an error if it can detect one. If it can't detect 
one, the compiler does not report any error. That implies, it is up to 
the implementation on how much it will try to detect such errors, apart 
from the obvious cases.

2. Current C++03 code is not affected by this proposal. The default is 
no compile-time checking is done, if no throw compile-time specification 
is provided.

3. Compile-time checking of throw compile-time specifications is done 
separately from throw run-time checking. That is, "throw" specifications 
remain as they are, run-time checked. New keywords are introduced for 
compile-time checking. There probably can be prettier names, but here I 
choose the keywords "_throw" and "_nothrow" for this purpose.



The above imply that:

1. The loose compile-time checking of _throw/_nothrow specifications is 
defined explicitly by the programmer if he chooses to do so. The default 
is no compile-time checking.

2. If an unexpected exception is thrown, it propagates as it didn't 
violate the compile-time _throw/_nothrow specifications, that is with no 
run-time side-effects. In other words, if the compile-time 
exception-specification is violated at run-time, std::unexpected() is 
*not* invoked.

3. The loose compile time checking will eventually lead to better error 
handling code, that is more safe code without any additional run-time cost.



Details:

1. The possible uses and meanings of "_throw" are:

_throw(), _throw(void): The function or member function does not throw 
any exception.

_throw(some_exception1, some_exception2): The function or member 
function may throw one of those exceptions. It may throw more that are 
defined in the _nothrow specification, but does not report them as a 
compile-time error (loose meaning).

_throw(...): Equivalent to non-existence of compile-time _throw 
specification. That is:


void somefunc()
{
   // ...
}

is equivalent to

void somefunc() _throw(...)
{
  // ...
}

_throw(...) is just more explicit.



2. The possible uses and meanings of "_nothrow" are:

_nothrow(), _nothrow(void): Equivalent to non-existence of compile-time 
_nothrow specification. That is the following are equivalent:


void somefunc()
{
  // ...
}


void somefunc()
{
  // ...
} _nothrow()


void somefunc()
{
  // ...
} _nothrow(void)



_nothrow(some_exception1, some_exception2): The compiler will ignore 
some_exception1 and some_exception2 and will not provide any 
compile-time error, if it detects that any of these two violate any 
_throw specification.


Example:

void somefunc() _throw(std::bad_alloc, my_app::graph_range_error)
{
   // ...
} _nothrow(std::out_of_range)



3. _nothrow(...): Ignore all exceptions. Compatible only with the 
_throw() specification.


Example:

void somefunc() throw()
{
   // ...
} nothrow(...)
0
ivranos1 (46)
1/21/2008 9:17:33 PM
Added a clarification:


Ioannis Vranos wrote:
> A revised version of my proposal (version 1.9):
> 
> 
> Design ideals:
> 
> 1. *Loose* compile-time checking of throw compile-time specifications. 
> The compiler reports an error if it can detect one. If it can't detect 
> one, the compiler does not report any error. That implies, it is up to 
> the implementation on how much it will try to detect such errors, apart 
> from the obvious cases.
> 
> 2. Current C++03 code is not affected by this proposal. The default is 
> no compile-time checking is done, if no throw compile-time specification 
> is provided.
> 
> 3. Compile-time checking of throw compile-time specifications is done 
> separately from throw run-time checking. That is, "throw" specifications 
> remain as they are, run-time checked. New keywords are introduced for 
> compile-time checking. There probably can be prettier names, but here I 
> choose the keywords "_throw" and "_nothrow" for this purpose.


4. The compile-time checking is *loose*, and will issue a compile-time 
error, only if it detects that a *specific* exception can be thrown that 
violates the _throw/_nothrow specifications, and will not be based on 
declaration hierarchy.




> The above imply that:
> 
> 1. The loose compile-time checking of _throw/_nothrow specifications is 
> defined explicitly by the programmer if he chooses to do so. The default 
> is no compile-time checking.
> 
> 2. If an unexpected exception is thrown, it propagates as it didn't 
> violate the compile-time _throw/_nothrow specifications, that is with no 
> run-time side-effects. In other words, if the compile-time 
> exception-specification is violated at run-time, std::unexpected() is 
> *not* invoked.
> 
> 3. The loose compile time checking will eventually lead to better error 
> handling code, that is more safe code without any additional run-time cost.
> 
> 
> 
> Details:
> 
> 1. The possible uses and meanings of "_throw" are:
> 
> _throw(), _throw(void): The function or member function does not throw 
> any exception.
> 
> _throw(some_exception1, some_exception2): The function or member 
> function may throw one of those exceptions. It may throw more that are 
> defined in the _nothrow specification, but does not report them as a 
> compile-time error (loose meaning).
> 
> _throw(...): Equivalent to non-existence of compile-time _throw 
> specification. That is:
> 
> 
> void somefunc()
> {
>   // ...
> }
> 
> is equivalent to
> 
> void somefunc() _throw(...)
> {
>  // ...
> }
> 
> _throw(...) is just more explicit.
> 
> 
> 
> 2. The possible uses and meanings of "_nothrow" are:
> 
> _nothrow(), _nothrow(void): Equivalent to non-existence of compile-time 
> _nothrow specification. That is the following are equivalent:
> 
> 
> void somefunc()
> {
>  // ...
> }
> 
> 
> void somefunc()
> {
>  // ...
> } _nothrow()
> 
> 
> void somefunc()
> {
>  // ...
> } _nothrow(void)
> 
> 
> 
> _nothrow(some_exception1, some_exception2): The compiler will ignore 
> some_exception1 and some_exception2 and will not provide any 
> compile-time error, if it detects that any of these two violate any 
> _throw specification.
> 
> 
> Example:
> 
> void somefunc() _throw(std::bad_alloc, my_app::graph_range_error)
> {
>   // ...
> } _nothrow(std::out_of_range)
> 
> 
> 
> 3. _nothrow(...): Ignore all exceptions. Compatible only with the 
> _throw() specification.


Example:

void somefunc() _throw()
{
   // ...
} _nothrow(...)
0
ivranos1 (46)
1/21/2008 10:54:04 PM
Ioannis Vranos wrote:
>
> void somefunc(void) throw()
>
> "Error: void somefunc(void) throw(): Wrong exception specification.
> somefunc can throw std::bad_alloc, std::range_error".
>
> That is make the compiler to check exception specifications
> for errors too.

As i know, folks time by time offer the idea. The checking is
possible, of course, but will reqiure changes of many properties of C+
+ exception handling paradigm, maybe properties of C++ core.

Unfortunately, there are many useful changes if C++, that can not be
resolved by std library, so all of the ideas go away :).

For your "void somefunc(void) throw()" declaration.

In the example, you are using declaration of "throw(type)" as logical
one, to tell programmers about expected types of exception. And
compiler, or course, easy can do compile tests according declarations
of all used function, as compiler do it for all parameters.

In the case of exception "Warning:" can be used instead of "Error:".

But you can guess, that exception handling require runtime overhead in
comparison with ordinary C-style function, but some function never
will throw, so they could be implemented without the overhead.

The C++ declaration "throw()" do the same - define the kind of
function implementation rather than declare logical type of
exception.

The overhead is the fact, that implementation of exception handling
_logically_ require from function _two_ separated returns - with
exception and without. The requirement _logically_ means, that
external context must pass to the function two addresses: ordinary
return point and exceptional return point.

But if the C-style function "somefunc throw()" does throw, the
exception can not be passed via boundaries of "somefunc", due to
absence of the _logically_ second return address - exceptional return
point, means caller is not ready to process exceptions. To call
"unexpected()" is only way to continue or it is better to say "only
way to exit".

I hope you have surprised, as if you find
    void somefunc(void) virtual() inline() throw();
declaration :). I want to say, that this is unexpected place of
linkage type keywords :)

One can see, that there are two types of exception declaration:
logical and hardware(linkage).

Declaration:

[hardware (linkage)]
[type]
[function name]
[(]
[list of parameters]
[)]
[logical]
[;]

hardware: virtual, inline, nothrow, extern "", stdcall, register, etc
logical: throw(type), const, etc

is best solution.

examples:

inline nothrow
void
somefunc(); //throw() is default here by nothrow

Here exit with unexpected() if somefunc throw, there is no hardware
overhead.

inline
void
somefunc(void)throw();

Here can add/replace throw std::unexpected_exception if somefunc
throw, there is hardware overhead, but process is reliable for
exceptions.

inline
void
somefunc(void)throw(my_type&);

Here can add/replace throw std::unexpected_exception if somefunc throw
unrelated to my_type, there is hardware overhead, but process is
reliable for exceptions.

One of the problem is necessity of C++ stacked exceptions instead of
single exception. In the case we must not replace original exception
with std::unexpected_exception instead of add the
std::unexpected_exception for original exception.

Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new
0
grizlyk1 (428)
1/22/2008 3:36:32 AM
Andrew Koenig wrote:
>
> The problem with this is that *any* expression that
> has the capability of causing undefined behavior
> has the capability of throwing any exception. =A0

And reliable code must be able to catch all of them with catch(...).

> In particular, this property means that an implementation
> is permitted to extend the language so that arithmetic
> errors, such as division by zero, throw exceptions.

And it is very strange, why the trivial operations with POD types
still are not reliable, and errors for the operations can not be
catched by catch(...).

C language does not catch them, just because C language has no
exceptions.

The hardware exceptions are async, but automatically can be synced at
the point of synced usage of failed result (assume compiler must know
about possible exception with the operation for concrete arch) or with
stacked exceptions, error can be delayed to end of nearest try block.

> What would you have such an implementation do about
> an arithmetic operation inside a throw() function
> that might overflow? =A0
>
> If the compiler complains about it, it is rejecting a program
> that might have nothing wrong with it.

Warnings can be used instead of errors, but programmer must be able to
explicitly express his desire to not throw or to throw or to throw
something concrete, and compiler must understand the desire.

Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new
0
grizlyk1 (428)
1/22/2008 4:07:38 AM
I think we are very close to finish it. I think We can make safe-solid 
source code.


Please everyone to contribute, so as to make an excellent compile-time 
checking exception specification mechanism.



Ioannis Vranos wrote:
> Added a clarification:
> 
> 
> Ioannis Vranos wrote:
>> A revised version of my proposal (version 1.9):
>>
>>
>> Design ideals:
>>
>> 1. *Loose* compile-time checking of throw compile-time specifications. 
>> The compiler reports an error if it can detect one. If it can't detect 
>> one, the compiler does not report any error. That implies, it is up to 
>> the implementation on how much it will try to detect such errors, 
>> apart from the obvious cases.
>>
>> 2. Current C++03 code is not affected by this proposal. The default is 
>> no compile-time checking is done, if no throw compile-time 
>> specification is provided.
>>
>> 3. Compile-time checking of throw compile-time specifications is done 
>> separately from throw run-time checking. That is, "throw" 
>> specifications remain as they are, run-time checked. New keywords are 
>> introduced for compile-time checking. There probably can be prettier 
>> names, but here I choose the keywords "_throw" and "_nothrow" for this 
>> purpose.
> 
> 

[REMOVED]
> 4. The compile-time checking is *loose*, and will issue a compile-time 
> error, only if it detects that a *specific* exception can be thrown that 
> violates the _throw/_nothrow specifications, and will not be based on 
> declaration hierarchy.
> 
> 
> 
> 
>> The above imply that:
>>
>> 1. The loose compile-time checking of _throw/_nothrow specifications 
>> is defined explicitly by the programmer if he chooses to do so. The 
>> default is no compile-time checking.
>>
>> 2. If an unexpected exception is thrown, it propagates as it didn't 
>> violate the compile-time _throw/_nothrow specifications, that is with 
>> no run-time side-effects. In other words, if the compile-time 
>> exception-specification is violated at run-time, std::unexpected() is 
>> *not* invoked.
>>
>> 3. The loose compile time checking will eventually lead to better 
>> error handling code, that is more safe code without any additional 
>> run-time cost.
>>
>>
>>
>> Details:
>>
>> 1. The possible uses and meanings of "_throw" are:
>>
>> _throw(), _throw(void): The function or member function does not throw 
>> any exception.
>>
>> _throw(some_exception1, some_exception2): The function or member 
>> function may throw one of those exceptions. It may throw more that are 
>> defined in the _nothrow specification, but does not report them as a 
>> compile-time error (loose meaning).
>>
>> _throw(...): Equivalent to non-existence of compile-time _throw 
>> specification. That is:
>>
>>
>> void somefunc()
>> {
>>   // ...
>> }
>>
>> is equivalent to
>>
>> void somefunc() _throw(...)
>> {
>>  // ...
>> }
>>
>> _throw(...) is just more explicit.
>>
>>
>>
>> 2. The possible uses and meanings of "_nothrow" are:
>>
>> _nothrow(), _nothrow(void): Equivalent to non-existence of 
>> compile-time _nothrow specification. That is the following are 
>> equivalent:
>>
>>
>> void somefunc()
>> {
>>  // ...
>> }
>>
>>
>> void somefunc()
>> {
>>  // ...
>> } _nothrow()
>>
>>
>> void somefunc()
>> {
>>  // ...
>> } _nothrow(void)
>>
>>
>>
>> _nothrow(some_exception1, some_exception2): The compiler will ignore 
>> some_exception1 and some_exception2 and will not provide any 
>> compile-time error, if it detects that any of these two violate any 
>> _throw specification.
>>
>>
>> Example:
>>
>> void somefunc() _throw(std::bad_alloc, my_app::graph_range_error)
>> {
>>   // ...
>> } _nothrow(std::out_of_range)
>>
>>
>>
>> 3. _nothrow(...): Ignore all exceptions. Compatible only with the 
>> _throw() specification.
> 
> 
> Example:
> 
> void somefunc() _throw()
> {
>   // ...
> } _nothrow(...)


Things remaining to be solved:


void somefunc() _throw()
{
   throw std::bad_alloc;
}

is probably an obvious compile time error case.


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)
{
   // ...
}
0
ivranos1 (46)
1/22/2008 10:07:06 AM
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)
{
  // ...
}
0
ivranos1 (46)
1/22/2008 11:08:37 AM
Ioannis Vranos wrote:
> 
> 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)
> {
>  // ...
> }



Compiler checks/errors:


Where the source code is available to the compiler, any 
_nothrow(some_exception) specification at a function/member function, 
must have at least one equivalent catch(some_exception) or catch(...) 
exception handler at the function definition.

For example:


void somefunc() _throw()
{
   int *p= new int[10];

} _nothrow (std::bad_alloc)


should be flagged as a compiler error, because there is no 
catch(std::bad_alloc) or catch(...) exception handler at the function 
definition.


The following should be correct:


1 .

void somefunc() try _throw()
{
   int *p= new int[10]

} _nothrow (std::bad_alloc)


catch(std::bad_alloc)
{
  // ...
}


2.

void somefunc() try _throw()
{
   int *p= new int[10]

} _nothrow (std::bad_alloc)


catch(...)
{
   // ...
}


3.

void somefunc() try _throw()
{
   int *p= new int[10]

} _nothrow (...)


catch(...)
{
   // ...
}
0
ivranos1 (46)
1/22/2008 12:15:27 PM
Ioannis Vranos wrote:
> 
> Compiler checks/errors:
> 
> 
> Where the source code is available to the compiler, any 
> _nothrow(some_exception) specification at a function/member function, 
> must have at least one equivalent catch(some_exception) or catch(...) 
> exception handler at the function definition.
> 
> For example:
> 
> 
> void somefunc() _throw()
> {
>   int *p= new int[10];
> 
> } _nothrow (std::bad_alloc)
> 
> 
> should be flagged as a compiler error, because there is no 
> catch(std::bad_alloc) or catch(...) exception handler at the function 
> definition.
> 
> 
> The following should be correct:
> 
> 
> 1 .
> 
> void somefunc() try _throw()
> {
>   int *p= new int[10]
> 
> } _nothrow (std::bad_alloc)
> 
> 
> catch(std::bad_alloc)
> {
>  // ...
> }
> 
> 
> 2.
> 
> void somefunc() try _throw()
> {
>   int *p= new int[10]
> 
> } _nothrow (std::bad_alloc)
> 
> 
> catch(...)
> {
>   // ...
> }
> 
> 
> 3.
> 
> void somefunc() try _throw()
> {
>   int *p= new int[10]
> 
> } _nothrow (...)
> 
> 
> catch(...)
> {
>   // ...
> }



Where the source code is available to the compiler, any 
_throw(some_exception) specification at a function/member function, must 
have at least one equivalent throw some_exception(); statement.

Remember each function/member function is a level, and its _throw 
specifications are about exceptions they explicitly throw, and not about 
"inherited" exceptions from other function/member function calls.


For example:


void somefunc() _throw(std::bad_alloc)
{

}


is a compiler error.



void somefunc() _throw(std::bad_alloc)
{
   throw std::bad_alloc();
}

is correct.



The following should be correct:


void somefunc() _throw()
{
  vector<int> vec(10);

  for (vector<int>::size_type i= 0; i< 10; ++i)
      vec.at(i)=5;
}


because it doesn't throw any exception by itself.




void somefunc() try _throw()
{
  vector<int> vec(10);

  for (vector<int>::size_type i= 0; i< 10; ++i)
      vec.at(i)=5;
} _nothrow (std::out_of_range)


catch(std::out_of_range)
{
   // ...
}


is correct and removes std::out_of_range from the exception 
"accumulation" list.
0
ivranos1 (46)
1/22/2008 1:11:18 PM
On 21 Jan, 15:12, "Andrew Koenig" <a...@acm.org> wrote:
>
> The problem with this is that *any* expression that has the capability of
> causing undefined behavior has the capability of throwing any exception.  In
> particular, this property means that an implementation is permitted to
> extend the language so that arithmetic errors, such as division by zero,
> throw exceptions.
>
> What would you have such an implementation do about an arithmetic operation
> inside a throw() function that might overflow?  If the compiler complains
> about it, it is rejecting a program that might have nothing wrong with it.
> If it doesn't, then you need to figure out how to change your requirement to
> permit such behavior.

Or, put another way, "I've just invoked undefined behaviour, and it
hasn't done what I expected".  ;-)

Surely the standard should be written in such a way as to make sense
when the user isn't invoking undefined behaviour.  That is, if you had
static exception checking, the following should compile cleanly:

  int divide( int x, int y )
    static throw() // or whatever syntax you adopt
  {
    return x / y;  // But what if y == 0?
  }

If y == 0, then you've got undefined behaviour.  If the compiler
chooses to define it to this behaviour to throw a "divide by zero"
exception, then that's fine.  However the compiler then also needs to
define what happens when it encounters an exception specification.
And the obvious choices seem to be: that it passes through the ES; or
that it is caught by the ES and std::unexpected (and probably then
std::terminate) is called.  Personally, I think I'd want the latter,
because I wouldn't want anything to emerge from a function declared
"throw()".

But the good thing about undefined behaviour is that it's undefined.

Anyway, are there any implementations that throw an C++ exception on
divide by zero?  Or on dereferencing a null or invalid pointer?  I
know about MSVC's "structured exceptions", but when I last looked
(some time ago: I don't really use Windows), these weren't like C++
exceptions in that they weren't caught by "catch (...)" or by a
"throw()" ES.

--
Richard Smith
0
richard949 (28)
1/22/2008 3:37:32 PM
On 2008-01-22 14:11, Ioannis Vranos wrote:
> Ioannis Vranos wrote:
>> 
>> Compiler checks/errors:
[...]

My advice to you is to take some time to study how it is done in Java,
C++, and possibly a few other languages, and also to read the articles
on the subject that is available on the 'net. Then sit down and think it
all through and finally present your idea in comp.programming (or maybe
comp.std.c++), because it seems to me like this is starting to go off-
topic (since this is really more related to language design than C++).

-- 
Erik Wikström
0
Erik-wikstrom (1881)
1/22/2008 5:30:24 PM
Erik Wikström wrote:
> 
> My advice to you is to take some time to study how it is done in Java,
> C++, and possibly a few other languages, and also to read the articles
> on the subject that is available on the 'net. Then sit down and think it
> all through and finally present your idea in comp.programming (or maybe
> comp.std.c++), because it seems to me like this is starting to go off-
> topic (since this is really more related to language design than C++).


Thanks for your advice. I 'll spend some time thinking on the subject 
and provide a complete proposal. I opened this discussion thread to see 
what others think, while I was thinking, like an open source project 
kind of thing. :-)

I am not going to check how Java does it, I am not willing to learn Java 
right now. I had my time with .NET back in VS 2002/2003 era, learned 
..NET multithreading and stuff, and more or less I consider it as a lost 
time. Java is a language syntax + framework, like C#/CLI/.NET, VB/.NET 
and perhaps C++/CLI/.NET.

Anyway, my approach in simple words is that a function/member function 
is like a level, its compile-time _throw specification denotes the 
exceptions the specific function/member function may throw itself and 
not the "inherited" exceptions of other called functions/member 
functions. And thus it requires at least one corresponding throw 
some_exception; statement for every _throw(some_exception), inside the 
function/member function definition, checked easily at compile-time.

The _nothrow keyword denotes which "inherited" exceptions are caught 
inside the definition of the function/member function and requires at 
least one catch(some_exception); statement inside the function/member 
function definition or a catch(...), easily checked at compile-time.

The non-caught exceptions types are "accumulated" at compile time and 
with the use of the :exceptions keyword the compiler will inform us with 
a message which uncaught exceptions may be thrown from a specific 
function call in the style:


int main()
{
   some_func() :exceptions;
}


so as to create the needed catch() blocks.


Anyway, I will write all down and come back with it after few weeks. In 
the meantime, I will check for any replies in this thread. :-)
0
ivranos1 (46)
1/22/2008 7:55:36 PM
Reply: