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

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/20/2008 12:16:47 PM
comp.lang.c++.moderated 10738 articles. 1 followers. allnor (8509) is leader. Post Follow

37 Replies
1284 Views

Similar Articles

[PageSpeed] 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.

It's been debated at great length, and has found no significant
support for reasons which a search of the archives should turn
up.  I'd be astonished if C++1x changed this, though I can just
about imagine it *might* deprecate or remove exception specs
other than throw().

-- James

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
James
1/20/2008 8:20:56 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.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/20/2008 8:23:18 PM
Ioannis Vranos ha scritto:
> Perhaps a mechanism can be introduced in the C++0x/1x standard,

I still prefer calling it C++0x and wish it can be approved before the
end of the decade...

> 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.
> 

Think about it. A function without exception specification can
potentially throw any exception. Most C++ library functions don't have
exception specifications. In addition to those functions borrowed from
the C library, there are a lot of cases of C++ functions with EH
guarantees that cannot be represented with exception specifications, for
example most STL algorithms. For instance:

   int foo(const std::vector<int> v) throw()
   {
      return std::accumulate(v.begin(), v.end(), 0);
   }

this wouldn't compile according to your proposal, because
std::accumulate does not have an exception specification. However this
specific instantiation of std::accumulate is guaranteed not to throw.
There's no way you can describe with exception specifications that
std::accumulate does not throw exceptions itself, but it might propagate
an exception thrown during the manipulation of an UDT passed as template
parameter.

Your proposal would actually make it impossible to call most library
functions from any function with an exception specification. And not
only direct calls, even calls at an arbitrary level of nesting.  In
practice, a function with an exception specification could barely
perform any non-trivial task.

Of course, I'm omitting the fact that your proposal would make illegal
practically every C++ program written so far with at least one exception
specification in it.

HTH,

Ganesh

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Alberto
1/20/2008 8:24:45 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)




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)

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/21/2008 8:51:41 AM
Alberto Ganesh Barbati wrote:
> Ioannis Vranos ha scritto:
>> Perhaps a mechanism can be introduced in the C++0x/1x standard,
> 
> I still prefer calling it C++0x and wish it can be approved before the
> end of the decade...
> 
>> 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.
>>
> 
> Think about it. A function without exception specification can
> potentially throw any exception. Most C++ library functions don't have
> exception specifications. In addition to those functions borrowed from
> the C library, there are a lot of cases of C++ functions with EH
> guarantees that cannot be represented with exception specifications, for
> example most STL algorithms. For instance:
> 
>   int foo(const std::vector<int> v) throw()
>   {
>      return std::accumulate(v.begin(), v.end(), 0);
>   }
> 
> this wouldn't compile according to your proposal, because
> std::accumulate does not have an exception specification. However this
> specific instantiation of std::accumulate is guaranteed not to throw.
> There's no way you can describe with exception specifications that
> std::accumulate does not throw exceptions itself, but it might propagate
> an exception thrown during the manipulation of an UDT passed as template
> parameter.


I have completed my proposal with some more details and I am answering
with those in mind too.

I am not sure why this wouldn't compile. AFAIK v.begin(), v.end(),
vector<int>::iterator do not throw any exceptions. So the compiler would
see that this is the case.


Also one can still do:


    int foo(const std::vector<int> v) throw()
    {
       return std::accumulate(v.begin(), v.end(), 0);
    } nothrow(...)


> Your proposal would actually make it impossible to call most library
> functions from any function with an exception specification. And not
> only direct calls, even calls at an arbitrary level of nesting.  In
> practice, a function with an exception specification could barely
> perform any non-trivial task.
> 
> Of course, I'm omitting the fact that your proposal would make illegal
> practically every C++ program written so far with at least one exception
> specification in it.


I am not so sure this is the case. If this was the case, I think the
compiler would have a point to provide compile-time errors if the
exception specifications are not accurate.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/21/2008 8:51:49 AM
Sean Hunt wrote:
> 

I have completed my proposal with some more details and I am answering
with those in mind too.


> 1) Because Java's checked exceptions are a chore. Sure, it seems
> great, but if you actually don't care (say, because you're a wrapper
> function), then you have to write out an entire specification. Or,
> even worse, you'll just add "throws Exception", and then your client
> has to deal with your idiocies.


I do not know how Java exceptions work, let's concentrate on my proposal
and its facts.



> 2) Implementability. Microsoft's compiler ignores all non-empty
> exception specifications. Other compilers refuse to implement
> functions with a non-empty exception sepcifications. This would be an
> implementation nightmare.


This sounds like broken compilers.


> 3) Templates. Imagine the following code:
> 
> template <typename T>
> void foo (T& t)
> {
>    t.bar();
> }
> 
> struct baz
> {
>    void bar () throw (SomeException) {};
> }
> 
> int main ()
> {
>    baz b;
>    foo(b);
> }
> 
> How is the template supposed to know that baz::bar might throw
> SomeException? Should it be an error? Should the template function
> have that implicitly added to the specification? What if it is a
> virtual function override (and hence cannot loosen the specification,
> even if it wants to)?


At first, the template can remain as it is. This will cause no
compile-time error since it is equivalent to:


  template <typename T>
  void foo (T& t) throw(...)
  {
     t.bar();
  }


Also even if we had the case:


  template <typename T>
  void foo (T& t) throw()
  {
     t.bar();
  }


If the compiler can not know if there is an error, then it will not
produce a compile-time error.

I remind that I am talking about loose application of the compile-time
checking. If the compiler is sure it is an error, then it will flag it
as a compile-time error, otherwise it will not provide a compile-time error.



> Checked exceptions have not been popular outside of Java; they have no
> place in C++


Let's not be biased because of a different implementation in Java.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/21/2008 8:52:06 AM
apm35@student.open.ac.uk wrote:
> 
> I eventually withdrew the proposal but kept it on line because the
> subject just keeps on coming up. Perhaps the OP and others may care to
> take another look. The main issue was making it work properly in the
> presence of templates.

I have completed my proposal with some more details and I am answering
with those in mind too.

Since those addtions haven't appeared in clc++m yet, I am posting the
complete proposal here too:


---------------------------------------------------------------------

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 somefunc() 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)


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(...)

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/21/2008 8:53:41 AM
Ioannis Vranos wrote:
> Sean Hunt wrote:
>>
> 
> I have completed my proposal with some more details and I am answering
> with those in mind too.
> 

I admire your tenacity but you are completely wasting your time. There
is no way that your proposal would be considered before the middle of
the next decade. In addition you would need to show how exceptions would
be completely checked at compile time. Some at compile time and some at
runtime will not buy anything useful (that is why people mention the
Java experience).

Also the Java experience adds weight to the belief that we cannot
produce full compile time checked exception specifications, Java
couldn't when it had no tie back to any legacy code. If you cannot do it
then what hope is there of doing it with a language like C++ with a
heavy legacy burden.

There is a small possibility that the proposed attribute syntax may
allow compile time check of a form of throws nothing but even that faces
an uphill struggle.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Francis
1/21/2008 1:52:42 PM
Ioannis Vranos wrote:
> 
> I do not know how Java exceptions work, let's concentrate on my proposal
> and its facts.

But that is, what you are proposing: java exception specifications.
So let's look at the consequences it had in the java world.

Before eclipse was popular, most java programmers wrote "throws
Exception" (which would be your throw(...)). Why was that so?
The programmers were lacking the time, the will, the overview or
diligence to do it more specifically.

Imagine you expand the exception spec of one function in one file, which
is called by five other functions spread over 3 files. some of those
have to be altered to catch the new exception. the other's specification
have to be expanded, requiring you to alter their callers.
In summary, if you change the specification of one of your basic
functions, you will have to edit almost every file in your project.

As one can see this is expensive and impractical. If you'd have
something like C++Eclipse, this would reduce to very annoying.

Let's however have a look at the benefits and drawbacks of exception
specifications, as they are now:

http://www.gotw.ca/publications/mill22.htm summarizes them as:

1. Enforce at runtime that functions will only throw listed exceptions.
2. Enable or prevent compiler optimizations having to check whether
listed exceptions are indeed being thrown.

Your compile time model perhaps could remove the current drawbacks,
leaving:

3. Enable compiler optimizations.

So your proposal would result in:

a) Add 10 characters ("throw(...)") to every function and nothing else
changes.
b) Greatly increase your coding effort hoping that your compiler then
produces slightly faster code.

I am sorry, but I cannot think of a reason, why I would want exceptions
like that. In your proposal you state what changes you want, but not
why. Perhaps you should clear up things a bit on that.
-- 
Sebastian Herbst

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Sebastian
1/21/2008 1:54:38 PM
Ioannis Vranos wrote:

> Sean Hunt wrote:
>> 
> 
> I have completed my proposal with some more details and I am answering
> with those in mind too.
> 
> 
>> 1) Because Java's checked exceptions are a chore. Sure, it seems
>> great, but if you actually don't care (say, because you're a wrapper
>> function), then you have to write out an entire specification. Or,
>> even worse, you'll just add "throws Exception", and then your client
>> has to deal with your idiocies.
> 
> 
> I do not know how Java exceptions work, let's concentrate on my
> proposal and its facts.

Then a short explanation of Java exceptions could be in order.
It gives something to compare your proposal against.

Java has two kinds of exceptions, checked and unchecked. The large
majority of exceptions in Java are of the checked kind. Unchecked
exceptions are mostly for situations that an application is not
supposed/required/able to anticipate.

With checked exceptions, the compiler verifies that for every exception
that could be generated by a method call (including constructors) or a
throw statement, the exception or its base-class *must* either
- be mentioned in a throws clause in the declaration, or
- caught within the function.

Additionally, methods that hide or override methods from a base-class
can not have a less strict exception specification than the
corresponding function in the base-class. (i.e. it may not throw
additional exceptions)

This is all verified at compile-time and results in a compile failure if
the requirements are not met.

> 
>> 2) Implementability. Microsoft's compiler ignores all non-empty
>> exception specifications. Other compilers refuse to implement
>> functions with a non-empty exception sepcifications. This would be an
>> implementation nightmare.
> 
> 
> This sounds like broken compilers.

As I think this was a deliberate choice, I personally would not use the
word 'broken' to describe them.
'broken' to me signifies that there exists an acknowledged bug in the
implementation of the compiler.
I would call these compilers just non-conforming in this aspect.

> 
>> 3) Templates. Imagine the following code:
>> 
>> template <typename T>
>> void foo (T& t)
>> {
>>    t.bar();
>> }
>> 
>> struct baz
>> {
>>    void bar () throw (SomeException) {};
>> }
>> 
>> int main ()
>> {
>>    baz b;
>>    foo(b);
>> }
>> 
>> How is the template supposed to know that baz::bar might throw
>> SomeException? Should it be an error? Should the template function
>> have that implicitly added to the specification? What if it is a
>> virtual function override (and hence cannot loosen the specification,
>> even if it wants to)?
> 
> 
> At first, the template can remain as it is. This will cause no
> compile-time error since it is equivalent to:
> 
> 
>   template <typename T>
>   void foo (T& t) throw(...)
>   {
>      t.bar();
>   }
> 

Therefor, each function calling foo also must have a 'throw all'
specification. Otherwise the compiler would see that foo can throw more
exceptions than the calling function is willing to handle/declare.

> 
> Also even if we had the case:
> 
> 
>   template <typename T>
>   void foo (T& t) throw()
>   {
>      t.bar();
>   }
> 
> 
> If the compiler can not know if there is an error, then it will not
> produce a compile-time error.

But the compiler *does* know, because the declaration of T::bar() must
be visible to be able to make the call.
Or are you suggesting that, contrary to the current requirements, an
exception specification can be provided only on some of the
declarations of a function?

> 
> I remind that I am talking about loose application of the compile-time
> checking. If the compiler is sure it is an error, then it will flag it
> as a compile-time error, otherwise it will not provide a compile-time
> error.

Unless your proposal would change the standard in this, the compiler
knows for every function that gets called what exception specification
that function has.
Currently, it is required that *every* declaration includes the
exception specification (if any).

> 
>> Checked exceptions have not been popular outside of Java; they have
>> no place in C++
> 
> Let's not be biased because of a different implementation in Java.
> 
It is not the specific implementation in Java, it is the whole concept
of statically checked exceptions that seems unpopular.

I regard this feature as one of the biggest failures in Java and as a
big pain in the back to use. And then have I not even used any kind of
generics in Java.

In practice, I see several ways that the Java checked exceptions are
being circumvented, because in practice they do not live up to their
promises.
These circumventions are:
- Overly generic specifications: As a base-class function does not know
what the overrides are going to throw, we just specify 'everything' and
put the burden on the caller.
- Gobbled exceptions: A base-class function does try to restrict what
can be thrown, but an overriding function suddenly has to cope with an
exception that was unforeseen by the base class. This exception can
therefor not be passed on to the caller, so it is typically just
absorbed without proper handling.

Bart v Ingen Schenau
-- 
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Bart
1/21/2008 3:55:35 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.

I think it is too late now to add statically checking of exceptions to
C++.
There is too much code around with the (implicit) 'throws anything'
specification and I don't see it happening that companies will start
retrofitting their released libraries with exception specifications
that they and their customers did not ask for. It just costs too much
without any clear revenue.

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

That is no problem.
If a function has an exception specification, it must also be present on
all prototypes.
Or more formally: All declarations (including the one that starts the
definition) must specify the same exception specification, if any.

<snip> 
>> 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.

I think that in practice, the feature will either be effectively unused
(like the current situation), or exception safety will decrease where
its use is made mandatory.

The decrease is because statically checked exception specifications can
force people to handle an exception at the wrong level (i.e. when
insufficient context is available for a proper resolution of the
problem) or people start to use overly broad specifications to avoid
this from occurring.

>> 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).

And how many companies/authors would do that? Modifying code that has
worked perfectly well for years without any real incentive.

>> 
>> 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.

But all it needs are the declarations. With static checking, a function
can not throw an other exception, because the compiler would catch that
as a violation when compiling the function itself.

>> 
>> 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.

Why does the code have to be available?
Or do you propose that the declarations of a function do not have to
agree on the exception specification?

If that is the case, how would the compiler treat code like this:

  void foo() throw(...);

  void foo() throw(int);

  void foo() throw(std::exception)
  {
    throw 42;     /* 1 */
    throw "BOO!"; /* 2 */
  }

Does this code constitute a violation of the exception specification?
Why or why not?
Would removing line 1 or line 2 make any difference?

>> 
>> 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)

Can you explain what you mean with 'exception that the compiler will
ignore'?
Do you mean that the will be implicitly caught? And if so, how will they
be handled?
Or do you mean that these should be ignored for the purpose of verifying
the exception specification? I.e. they can occur, but the compiler
should not complain about them not being caught or declared in the
exception specification.
How should these be treated if they actually occur at runtime?

<snip - examples> 

Bart v Ingen Schenau
-- 
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Bart
1/21/2008 3:55:43 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(...)

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/21/2008 3:56:04 PM
Bart van Ingen Schenau wrote:
>
>> At first, the template can remain as it is. This will cause no
>> compile-time error since it is equivalent to:
>>
>>
>>   template <typename T>
>>   void foo (T& t) throw(...)
>>   {
>>      t.bar();
>>   }
>>
> 
> Therefor, each function calling foo also must have a 'throw all'
> specification. Otherwise the compiler would see that foo can throw more
> exceptions than the calling function is willing to handle/declare.


(I have provided a revision on my proposal and my answers will be based
on those, so as to be uptodate).

Not necessarily.


At first, this:

template <typename T>
void foo (T& t) _throw(...)
{
    t.bar();
}

is equivalent to this:

template <typename T>
void foo (T& t)
{
    t.bar();
}


In both cases:

void somefunc() _throw()
{
   some_class obj;

   foo(obj);
}


If the compiler *can* detect a *specific* exception that can be thrown
and violates the _throw() specification, it should issue a compile-time
error. If it *can't* detect a *specific* exception that can be thrown,
it will not issue a compile-time error. That is, *loose* compile-time
checking.



> Unless your proposal would change the standard in this, the compiler
> knows for every function that gets called what exception specification
> that function has.
> Currently, it is required that *every* declaration includes the
> exception specification (if any).


The compile-time checking will be *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.


In addition, if the compiler spots one or more *specific* exceptions
that can be thrown that violate our _throw specification one an use:

void somefunc() _throw()
{
   some_class obj;

   foo(obj);
} _nothrow(some_exception1, some_esception2)


or

void somefunc() _throw()
{
   some_class obj;

   foo(obj);
} _nothrow(...) // Ignore all exceptions that violate the _throw()
                 // specifications



-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/21/2008 10:58:28 PM
Added more info:


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(...)


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/21/2008 10:58:59 PM
Ioannis Vranos wrote:

> If the compiler *can* detect a *specific* exception that can be thrown
> and violates the _throw() specification, it should issue a compile-time
> error. If it *can't* detect a *specific* exception that can be thrown,
> it will not issue a compile-time error. That is, *loose* compile-time
> checking.
> 

So you are proposing that we break just about every piece of c++ code
ever written :) It ain't going to happen.

In the context of your proposal anyone adding an exception specification
to their function would be certifiable.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Francis
1/22/2008 5:22:12 AM
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.
> 


Of course it is. For example:

void foo()throw(something){
    myraiitype mt;
}

Used to compile fine, Now it fails because myraiitype (that is a RAII
class) has no ES on its ctor (well if I wrote it today it wouldn't). And
how do you propose we place ES on implicit ctors?

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Francis
1/22/2008 6:16:29 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 more info:
> 
> 
> 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)
{
   // ...
}

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/22/2008 6:16:38 AM
Ioannis Vranos wrote:
> 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 more info:
>>
>>
>> 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)
> {
>   // ...
> }


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(...)
{
   // ...
}



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.


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/22/2008 1:43:38 PM
Ioannis Vranos wrote:
> 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.
> 


FOFL, are you reading the responses to your posts? Unless I am missing
something no one else is supporting your proposal and even had you made
it 4 years ago when the coming release of C++ was under development
(rather than being finalised prior to being put out for vote and
comment) I doubt that it would have received much positive support. No
member of the Standards Committees that I have ever talked with supports
general exception specifications and some even question the utility of
the throw() case.

If you want this change you have a dozen years (it isn't going to go
into any quick revision or normative addendum that we are considering on
a shorter time-scale) to refine it and produce a working implementation
that demonstrates that it is feasible and useful.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Francis
1/22/2008 1:45:29 PM
Francis Glassborow wrote:
> 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.
>>
> 
> 
> Of course it is. For example:
> 
> void foo()throw(something){
>    myraiitype mt;
> }
> 
> Used to compile fine, Now it fails because myraiitype (that is a RAII
> class) has no ES on its ctor (well if I wrote it today it wouldn't). And
> how do you propose we place ES on implicit ctors?



You didn't read the revised proposal, didn't you?

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/22/2008 1:45:41 PM
Francis Glassborow <francis.glassborow@btinternet.com> wrote in
news:aqSdnb1DDfvMKwjanZ2dnUVZ8sWhnZ2d@bt.com:

> 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.
>> 
> 
> 
> Of course it is. For example:
> 
> void foo()throw(something){
>     myraiitype mt;
> }
> 
> Used to compile fine, Now it fails because myraiitype (that is a RAII
> class) has no ES on its ctor (well if I wrote it today it wouldn't).
> And how do you propose we place ES on implicit ctors?
> 

This isn't meant to say that I am in favor of the proposal but couldn't
you use the union of the ESs for all the classes members that are not
caught in other ways? A sort of implicit ES.

class mythrowingType{
     	mythrowingType()throw(RandomException);
};
class mythrowingType2{
     	mythrowingType2()throw(RandomException2);
};

class myraiitype{
     	mythrowingType  t;
     	mythrowingType2 t2;

};

myraiitype() would an ES  that it could throw RE or RE2. Not that this
would help with the code you posted not compiling. Unless RE and RE2 were
both types of 'something'.

I haven't bothered reading the whole thread but this would just leave you
to deal with explicit ESs that confilct with the implicit ones.

I certainly would not mind having this info as a WARNING. Particularly if
the "Die Program! Die" behavior you get with ES violations was toned down
somehow. For example, if all the ES did was provide hints to the compiler
and produced warnings on violations.

Otis



-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Otis
1/22/2008 2:26:15 PM
In article <fn4qbk$15og$3@ulysses.noc.ntua.gr>,
ivranos@no.spamfreemail.nospam.gr says...

[ ... ]

> You didn't read the revised proposal, didn't you?

I've read at least three. To me indicate that you don't understand the
problem and you haven't even made a minimal attempt at studying nearly
identical attempts at solutions (that have failed miserably).

Consider a law to reduce dependency on petroleum products by mandating
that all new automobiles use only helium as fuel by next year. First of
all, this would require technology (car-sized nuclear fusion for power)
that doesn't exist yet. Second, even if it worked, the result would be
dependence on a material that's even scarcer than what we're using now.

Your proposal is much the same way: nobody knows how to make it work,
and even if they did, the result would be much worse than what we have
now.

-- 
     Later,
     Jerry.

The universe is a figment of its own imagination.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Jerry
1/22/2008 10:31:10 PM
Francis Glassborow ha scritto:
> Ioannis Vranos wrote:
>> 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.
> 
> FOFL, are you reading the responses to your posts? Unless I am missing
> something no one else is supporting your proposal and even had you made
> it 4 years ago when the coming release of C++ was under development
> (rather than being finalised prior to being put out for vote and
> comment) I doubt that it would have received much positive support. No
> member of the Standards Committees that I have ever talked with supports
> general exception specifications and some even question the utility of
> the throw() case.
> 
> If you want this change you have a dozen years (it isn't going to go
> into any quick revision or normative addendum that we are considering on
> a shorter time-scale) to refine it and produce a working implementation
> that demonstrates that it is feasible and useful.
> 

FWIW, I believe Francis' recap of the situation is totally correct.

Ganesh

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Alberto
1/22/2008 10:32:10 PM
When speaking about the semantics of an exception specification, I will
refer to an absent specification as a 'throw all' specification.

Richard Smith wrote:

> I think you could do this quite effectively, and in a way that doesn't
> break in templates, by adding optional static checking of ESs and
> optional implicit generation of ESs.  For the sake of argument, I'm
> prefixing the ES with "static" for a statically-checked ES, and
> putting "auto" as the only thing in parentheses for a implicit ES:
> 
>    exception-specification:
>      static[opt] throw( type-list-id[opt] )
>      static[opt] throw( auto )
> 
> 
> 
> To implicitly generate an ES for a function, the compiler looks at the
> ESs of all of the functions called and throw expressions (even in
> provably unreachable code) other than in handlers.  We can pretend
> that a re-throw expression is just a call to a function with no ES,
> and other throw expressions are calls to a function with one type in
> their ES: the type being thrown.  Functions and throw expressions
> within try blocks have their ESs modified by any no-throw handlers
> (catch blocks).  A no-throw handler is a handler that only calls
> functions with a no-throw ES.

I agree with Sean Hunt that a re-throw that is not lexically part of a
catch-clause should not render a function to be implicitly 'throw all'.

<snip - union of exception specifications>

> Static checking is now easy: if implicit ES is non-existent (i.e.
> something called a function without an ES and didn't catch(...)), or
> if the implicit ES is weaker than the supplied one, it is an error.
> The former means that it makes sense for a ES to be both statically-
> checked and implicitly-generated: i.e. a "static throw(auto)"
> declaration: this just checks that the ES exists.
> 
> 
> 
> How does this work recursively if a function with an implicitly-
> generated ES calls another one with an implicitly-generated ES,
> perhaps in a different translation unit?
> 
> As a implicitly-generated ES can only be generated when the function
> definition is visible, a function *declaration* with a implicitly-
> generated ES behaves as if it does not have an ES; but a function
> *definition* with an implicitly-generated ES does have one.  Thus,
> 
>    void f() throw(auto);
>    // ...
>    void f() throw(auto) { throw A; }
> 
> is equivalent to
> 
>    void f();
>    // ...
>    void f() throw(A) { throw A; }
> 
> except that you won't get an error about differing exception
> specifications.

This would cause a problem if you call a function with throw(auto) for
which the definition is not visible.

For example:
   void f() throw(auto);
   void g() static throw(auto)
   {
     f();
   }

Under the current proposal, this would result in a diagnostic that the
calculated exception specification for g() is 'throw all' and that is
not allowed for a throw(auto) function.
To avoid this, I would propose the following changes:
- If the definition of a throw(auto) function is not visible, the
throw(auto) specification is interpreted as specifying a bounded set of
unspecified exceptions. These unspecified exceptions will not match any
explicitly mentioned exceptions in a caller (either in the exception
specification or in a catch clause), but they can be added to the
automatically generated set of exceptions for the purpose of
calculating a throw(auto) specification.
- If the definition of a throw(auto) function is visible, the
throw(auto) specification is replaced with the calculated throw
specification.

This way, the example above would be valid, while the one below will be
rejected.

   void f() throw(auto);
   void g() static throw(std::bad_alloc)
   {
     f();
   }
   voif f() throw(auto)
   {
     int* i = new int;
   }
/* Switching the definitions of f() and g() would make this code valid
*/

> 
> How would this work in practice?
> 
> Adding no-throw ESs effectively really needs to be done from the
> bottom up -- starting with the lowest level libraries.  So it would be
> good to see no-throw ESs in the standard library where that guarantee
> exists (e.g. vector::size, shared_ptr copy constructor, and
> particularly on iterator operations).   Light-weight algorithms
> probably just want a throw(auto) ES adding, and heavier duty code
> (e.g. large parts of IOStreams, Locales) probably don't want any ESs.

I see a couple of problems with the practicalities.
The biggest one is C language APIs. We can not expect that libraries
that only provide an API for the C language will start adding exception
specifications to their headers.
I see two possible solutions to this problem
- we create a syntax to provide the exception specification en-masse,
like it is currently for the language linkage specification (extern "C"
{ ... }), or
- as an exception to the general rules, extern "C" functions without an
exception specification are assumed to have a specification of throw().

<snip - interaction with concepts>

> To me this sounds quite a workable way of improving exception
> specifications.  Although it's all formulated in terms of general ESs,
> it's really only the no-throw case that I think is of use.  But given
> the starting point, it seems simpler to formulate it this way.
> 
> The big benefit is overloading based on whether something can throw an
> exception -- i.e. being able to supply several versions of an
> algorithm with different complexities depending on what might throw.
> It's entirely backwards compatible: ordinary "throw(foo)" declarations
> are unchecked and result in std::terminate as today.  You don't pay
> for what you don't use: if you don't use implicitly-generated or
> statically-checked ESs, they are not computed; nor do they need
> computing for functions with implicitly-generated ESs that you call.
> 
> Does anyone have any thoughts on it?
> 
In general, I rather like it.
There are some details that I don't agree on, and the interaction with
concepts and the overloading on ES goes a bit over my head, but it is
at least better than the other proposals I have seen in this direction.
The main advantages for me are that it is optional, and that it does not
overly burden the authors of templates.

Bart v Ingen Schenau
-- 
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Bart
1/25/2008 9:45:34 AM
> Ioannis Vranos ha scritto:
>> Perhaps a mechanism can be introduced in the C++0x/1x standard,
>
> I still prefer calling it C++0x and wish it can be approved before the
> end of the decade...

It always can end as C++ 0xA, 0xB... ;)
In my opinion new features should not be added. At some moment you have
to say "stop", even if the moment is not perfect.

regards,
Jakub



-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Jakub
1/25/2008 9:45:48 AM
Greg Herlihy wrote:

>> For instance:
>>
>>    int foo(const std::vector<int> v) throw()
>>    {
>>       return std::accumulate(v.begin(), v.end(), 0);
>>    }
>>
>> this wouldn't compile according to your proposal, because
>> std::accumulate does not have an exception specification.
> 
> Not necessarily. Presumably the absence of an exception specification
> would mean - as it does in Java - that the function, foo() in this
> case, does not throw (or propagate) any exceptions.

(I hope I haven't misunderstood Alberto's example)

That's the whole idea, you cannot know when you write "accumulate" (being a
template function) if it will throw or not (it depends on the actual
arguments used for instantiation). So you are saying that a function
without exception specification as "accumulate" means it does not throw,
but "accumulate" may throw. So you cannot establish at compile time that a
template function may throw or not until the code is instantiated but when
the code is instantiated it is already too late (the "accumulate" generic
definition has a throw specification that needs to be the same for any
instantiation of "accumulate", right?)

> Such programs are few and far between. The truth is that exception
> specifications are simply not useful - so there is little point in
> keeping them in C++. So the logical choices are either to deprecate
> them and or to make them useful by adding compile-time checks.

I think most people agree that "throw()" is still useful but the rest
probably not. And I don't find having them checked at compile time any more
useful.

PS: I find exception specifications not checked at compile time mostly a
problem of code that is not written keeping in mind "anything" may throw,
and AFAIK I don't write such code so it does not affect me; write all your
code to be exception safe, do not copy when it may fail (so don't much such
types Copyable, make them Movable or use them with reference semantics),
don't throw from dtors (log such errors instead) and you should be fine

-- 
Dizzy


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
dizzy
1/25/2008 9:45:59 AM
"Ioannis Vranos" <ivranos@no.spamfreemail.nospam.gr> wrote in message
news:fn1ueu$khd$1@ulysses.noc.ntua.gr...

>>   int foo(const std::vector<int> v) throw()
>>   {
>>      return std::accumulate(v.begin(), v.end(), 0);
>>   }

> I am not sure why this wouldn't compile. AFAIK v.begin(), v.end(),
> vector<int>::iterator do not throw any exceptions. So the compiler would
> see that this is the case.

The problem is that std::accumulate might throw an exception, and the
compiler has no way of knowing that it won't.

For that matter, if the particular implementation extends the language to
say that integer overflow throws an exception, then std::accumulate might
well throw an exception and this example should not compile.


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Andrew
1/25/2008 9:46:20 AM
Andrew Koenig wrote:
> "Ioannis Vranos" <ivranos@no.spamfreemail.nospam.gr> wrote in message
> news:fn1ueu$khd$1@ulysses.noc.ntua.gr...
> 
>>>   int foo(const std::vector<int> v) throw()
>>>   {
>>>      return std::accumulate(v.begin(), v.end(), 0);
>>>   }
> 
>> I am not sure why this wouldn't compile. AFAIK v.begin(), v.end(),
>> vector<int>::iterator do not throw any exceptions. So the compiler would
>> see that this is the case.
> 
> The problem is that std::accumulate might throw an exception, and the
> compiler has no way of knowing that it won't.
> 
> For that matter, if the particular implementation extends the language to
> say that integer overflow throws an exception, then std::accumulate might
> well throw an exception and this example should not compile.


In my proposal, each function/method is a level and the compile-time
_throw specification declares which exceptions the specific
function/method will throw and not the "inherited" ones.

Here a summary of my proposal follows as posted in c.l.c++. After few
weeks I will write my proposal down carefully, and will post it here
along with links to OpenOffice and Powerpoint presentation files (made
with OpenOffice.Org of course! :-) ).


Here is my last message about this proposal that I posted in c.l.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. :-)

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ioannis
1/26/2008 12:04:39 AM
> You didn't read the revised proposal, didn't you?

If you have had to revise 3 times while here, it suggests you didnt put
sufficient thought in your 1st proposal. I would have studied others
languages (like Java) that do have have exceptions and find out if they 
have
been a success/failure and the pros/cons of their solutions.

There is an excellent chapter in Bjarnes "Design and Evolution of C++" 
where
it considers proposed new features and many design criterion questions that
are designed to evaluate how realistic your proposal is to being added to
C++. It is also good for weeding out bad proposals that have no hope of 
ever
being added to the language.

Cheers

Stephen Howe



-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Stephen
1/26/2008 4:04:10 AM
Ioannis Vranos wrote:

> 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. :-)
> 

Believe me please, you are wasting your time (which might be spent on
something else). It will not be considered for the coming release of C++
and that together with other work already in the pipeline will so change
the landscape that you might even be tackling a non-problem.

I have been involved with ES since about 1992 when I was a participant
in the BSI's C++ panel and we decided that ES without static enforcement
was without any value. Unfortunately our delegation to WG21 were
persuaded  that we were wrong. I think experience since shows we were
right. However no one I have talked with has ever claimed that there is
a realistic way in the context of separate compilation and templates to
check ES at compile time.

ES makes a promise which turns out to be a mirage.

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Francis
1/26/2008 8:01:52 AM
Richard Smith wrote:

> On 25 Jan, 15:45, Bart van Ingen Schenau <b...@ingen.ddns.info> wrote:

<snip>
>> For example:
>>    void f() throw(auto);
>>    void g() static throw(auto)
>>    {
>>      f();
>>    }
>>
>> Under the current proposal, this would result in a diagnostic that
>> the calculated exception specification for g() is 'throw all' and
>> that is not allowed for a throw(auto) function.
> 
> It's allowed for a "static throw(auto)" function -- it would be
> allowed for a "throw(auto)" function.  But yes, this is correct: I
> want a diagnostic from the code above.

You have convinced me that my counter-proposal on this point did not
anything.

<snip - discussion on throw(auto) >
>> > How would this work in practice?
>>
>> > Adding no-throw ESs effectively really needs to be done from the
>> > bottom up -- starting with the lowest level libraries.  So it would
>> > be good to see no-throw ESs in the standard library where that
>> > guarantee exists (e.g. vector::size, shared_ptr copy constructor,
>> > and
>> > particularly on iterator operations).   Light-weight algorithms
>> > probably just want a throw(auto) ES adding, and heavier duty code
>> > (e.g. large parts of IOStreams, Locales) probably don't want any
>> > ESs.
>>
>> I see a couple of problems with the practicalities.
>> The biggest one is C language APIs. We can not expect that libraries
>> that only provide an API for the C language will start adding
>> exception specifications to their headers.
> 
> Well, do bear in mind that C *can* propagate exceptions: std::qsort is
> an example.  But yes, you're right that most C can't throw or
> propagate exceptions.

Something tells me that it is UB to propagate an exception through an
extern "C" function, but I could not quickly find a reference that
actually says so.

If my memory serves me right, there is at least one major compiler
(MSVC) that would not handle the case gracefully, due to the way they
implemented the stack unwinding mechanism.

> 
> I'm not sure how big an issue this really is, though.  In my
> experience most good C++ will write its own light-weight wrapper
> around C libraries it uses to give it a more natural C++ feel.  Often
> converting error codes to exceptions is one thing it will be doing.
> 
> But in any case, I *don't* see this form of static exception checking
> as something that will pervade the whole of a program.  Really, it's
> only low-level pieces of code where I would expect to encounter any of
> this.

And I see calls to a C API also being restricted to the same lower
levels of the program. And people don't alway bother with writing a
wrapper if it will be especially thin.

And perhaps you don't expect the static checking to pervade the whole
program, but other may think otherwise. See what has happened to
templates that the original authors could never imagine.

> 
>> I see two possible solutions to this problem
>> - we create a syntax to provide the exception specification en-masse,
>> like it is currently for the language linkage specification (extern
>> "C"
>> { ... }), or
> 
> Perhaps.  But I don't think it's that big an issue.

The issue that I see is that, as has been said before in this thread,
static checking of ES has to be implemented from the bottom up.
What a shame it is then that the wrapper around the very useful and
often called C API has to use the old, *runtime checked*, ES in order
to make it possible to implement the statically checked ES on the
layer(s) above.
That would be sufficiently of a deterrent to forget about ES-es
altogether and leave things as they are now.

> 
>> - as an exception to the general rules, extern "C" functions without
>> an exception specification are assumed to have a specification of
>> throw().
> 
> No.  It would break existing code.  Think std::qsort, or most other
> code that takes a function pointer.

I am not sure that such code is actually valid, biut it is indeed
something to take into account.

> 
> Thanks for your comments.
> 
> --
> Richard Smith
> 
Bart v Ingen Schenau
-- 
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Bart
1/28/2008 1:55:46 PM
On Tue, 2008-01-22 at 05:22 -0600, Francis Glassborow wrote:
> Ioannis Vranos wrote:
> 
> > If the compiler *can* detect a *specific* exception that can be thrown
> > and violates the _throw() specification, it should issue a compile-time
> > error. If it *can't* detect a *specific* exception that can be thrown,
> > it will not issue a compile-time error. That is, *loose* compile-time
> > checking.
> > 
> 
> So you are proposing that we break just about every piece of c++ code
> ever written :) It ain't going to happen.
> 
> In the context of your proposal anyone adding an exception specification
> to their function would be certifiable.
> 

I think (from using Java, suffering problem 2 below, and Haskell, not
suffering problem 1 below in some cases and irritatingly suffering it in
others) the two big problems with statically checked exception
specifications are:

   1) The typing limitations of C++ (eg, vector::begin() and
vector::end() on a vector of size > 0 have the same type even though one
can be incremented and dereferenced and the other can't) and,
   2) the runtime polymorphism of OOP which allows (sensibly) additional
exceptions to be thrown by methods of derived classes. This means that
the caller knows the exact type and thus wants to be told of the
specific exception, having knowledge of the extended list of receivable
exceptions, yet the callee knows nothing of the additional exceptions
and to be useful *must* forward them on.

1 is a problem for generic programming and perhaps Concepts could be
used to enhance exception specifications in this context somewhat
similarly to my solution to 2, if my solution to 2 doesn't also work for
that case immediately.

2 could perhaps be solved with a type set algebra for/in exception
specifications, something like:

class MyClass {
public:
   virtual throw io_exceptions = block_error | char_error; // union

   virtual void writesomething(unsigned char* data, size_t size)
     throw(io_exceptions) = 0; // callers know to pass on additional
   // exceptions because the exception spec is a "virtual" set
};

class MyNetworkClass : public MyClass {
   throw io_exceptions = MyClass::io_exceptions | network_error; // union
   // can be extended because it's a virtual throwset definition

   void writesomething(unsigned char* data, size_t size)
     throw io_exceptions { ... defn ... }
};

void fn(MyClass&& obj) throw(obj.io_exceptions | bad_alloc)
                    // caller and "fn" each evaluate the exception
                    // specification in the context of their
                    // respective knowledge
                    // eg, "fn" knows to support forwarding
                    // of unknown exceptions because
                    // obj.io_exceptions is virtual
{
.... defn ...
}

where a function just passes the object reference along to another
function, it could reference the exception specification of the function
that it calls (or a common named throwset defintion)

Since throw is never used in class definition context and the existence
of this use could be used to switch static typechecking on for callers
of methods of that class and its subclasses, as well as callers of
functions that use that classes name, also using the set algebra in a
throw specification (as above) would make callers use strict checking.
Thus preserving behaviour of existing programs and doing the expected
thing when using static checked code with old code in the same program.

-- 
Tristan Wibberley

Any opinion expressed is mine (or else I'm playing devils advocate for
the sake of a good argument). My employer had nothing to do with this
communication.



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Tristan
2/10/2008 12:01:34 PM
On Mon, 2008-01-21 at 22:58 -0600, Ioannis Vranos wrote:

> 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.

loose compile-time checking would be extraordinarily dangerous, and
IMHO, should never be considered. Compile-time checking should be all or
nothing (or rather "all compile-time when requested and all run-time
everywhere else"). If that means "check all throw() and check nothing
else" so be it.

Experience C++ developers will have problems working with this and
inexperienced developers wouldn't stand a chance.

-- 
Tristan Wibberley

Any opinion expressed is mine (or else I'm playing devils advocate for
the sake of a good argument). My employer had nothing to do with this
communication.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Tristan
2/10/2008 6:21:46 PM
On Feb 18, 1:49 pm, Pavel Minaev <int...@gmail.com> wrote:
> On Feb 17, 5:12 pm, mai...@gmail.com wrote:
>
> It's quite the opposite. Restriction on exception specs on inheritance
> follow directly from Liskov substitution principle, which as about as
> OOP as it can be. If a override implementation of a function throws
> some exception that the original function did not specify as throwing,
> then how can one safely do polymorphic calls to such a function at
> all?

With all the post-Java OOP languages - you have to assume that
anything may be thrown. The problem is that extending functionality by
deriving naturally leads to more branches and thus more things to go
wrong. The creator of an interface can't know what things derived
types would want to throw because they can't know what the derived
classes might do.

The problem is fundamentally rooted in typical programming languages
not providing a method to describe the types of values fully (and the
types of objects actually change over time as their values change -
but C++ doesn't really speak much of types, speaking, instead, of
classes). Fortunately, in so far as it extends to exception
specifications, I think the typesystem can be extended for a useful
set of use-cases.

It is my desire to support natural OOP style with strong typing that
has led me to suggest this.

> > This whole issue is normally avoided in C++ when doing OOP by not
> > using exception specifications.
>
> I wouldn't say people don't use specs for that reason by everyone.
> Personally, I don't use them because they don't enforce throw spec
> compliance at compile-time, and doing it at runtime serves little
> point - as far as I'm concerned, there's little difference between not
> catching an unexpected exception, and having it caught for you and
> unexpected() called. I want to know if my code is safe before it runs.

Calling std::unexpected is actually worse, throwing a disallowed
exception acts like it first removes all destructors from the cleanup
schedule and /then/ throws.

--
Tristan Wibberley

Not my employer's opinion, etc.


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
maihem
2/19/2008 4:46:22 PM
maihem@gmail.com wrote:
> On Feb 18, 1:49 pm, Pavel Minaev <int...@gmail.com> wrote:
>> On Feb 17, 5:12 pm, mai...@gmail.com wrote:
>>
>> It's quite the opposite. Restriction on exception specs on
>> inheritance follow directly from Liskov substitution principle,
>> which as about as OOP as it can be. If a override implementation
>> of a function throws some exception that the original function did
>> not specify as throwing, then how can one safely do polymorphic
>> calls to such a function at all?
>
> With all the post-Java OOP languages - you have to assume that
> anything may be thrown. The problem is that extending functionality
> by deriving naturally leads to more branches and thus more things
> to go wrong. The creator of an interface can't know what things
> derived types would want to throw because they can't know what the
> derived classes might do.

What do you mean?!

The creator of the derived classes pretty damn well has to follow the
requirements of the interface. How am I to call a virtual function, if
I don't know what it does?

>>
>> I wouldn't say people don't use specs for that reason by everyone.
>> Personally, I don't use them because they don't enforce throw spec
>> compliance at compile-time, and doing it at runtime serves little
>> point - as far as I'm concerned, there's little difference between
>> not catching an unexpected exception, and having it caught for you
>> and unexpected() called. I want to know if my code is safe before
>> it runs.
>
> Calling std::unexpected is actually worse, throwing a disallowed
> exception acts like it first removes all destructors from the
> cleanup schedule and /then/ throws.
>

If a function has proven a breach of contract, by throwing an
exception the throw spec says is never should, this function cannot be
trusted in any other way either. How do we know that any values
touched by it is correct?

Better pull the emergency break, as fast as possible!


Bo Persson



-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Bo
2/19/2008 10:05:15 PM
On Tue, 2008-02-19 at 22:05 -0600, Bo Persson wrote:
> maihem@gmail.com wrote:
> > On Feb 18, 1:49 pm, Pavel Minaev <int...@gmail.com> wrote:
> >> On Feb 17, 5:12 pm, mai...@gmail.com wrote:

> > With all the post-Java OOP languages - you have to assume that
> > anything may be thrown. The problem 
                                 ^^^^^^^
I should have said "reason".

> is that extending functionality
> > by deriving naturally leads to more branches and thus more things
> > to go wrong. The creator of an interface can't know what things
> > derived types would want to throw because they can't know what the
> > derived classes might do.
> 
> What do you mean?!
> 
> The creator of the derived classes pretty damn well has to follow the
> requirements of the interface. How am I to call a virtual function, if
> I don't know what it does?

Would you forbid deriving with virtual functions? You need to know what
it does after all and embellishing the behaviour of the class would make
it do something different. Of course, with OOP, embellishment is the
*intent* and classes performing the same task via a different resource
will have different error conditions, which the top-level loop needs to
know about, yet the intermediate parts of the call stack will not know
about, should not know about, and (for a well-managed large
project) /can/not know about.


> > Calling std::unexpected is actually worse, throwing a disallowed
> > exception acts like it first removes all destructors from the
> > cleanup schedule and /then/ throws.
> >
> 
> If a function has proven a breach of contract, by throwing an
> exception the throw spec says is never should, this function cannot be
> trusted in any other way either. How do we know that any values
> touched by it is correct?

How do you /ever/ know that.

> Better pull the emergency break, as fast as possible!

Better to not compile the damned thing. Pulling the emergency break
means not cleaning up and not explaining things to the user. The only
thing that gets done is that O/S level resources are released.

The fact that the Java way was tried and almost universally abandoned
among its succeeding competitors should be evidence enough that an
interface designer shouldn't be expected to know what error paths all of
its implementations will have.

-- 
Tristan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Tristan
2/21/2008 10:33:04 PM
Tristan Wibberley wrote:
> On Tue, 2008-02-19 at 22:05 -0600, Bo Persson wrote:
>> maihem@gmail.com wrote:
>>> On Feb 18, 1:49 pm, Pavel Minaev <int...@gmail.com> wrote:
>>>> On Feb 17, 5:12 pm, mai...@gmail.com wrote:
> 
>>> With all the post-Java OOP languages - you have to assume that
>>> anything may be thrown. The problem...

    The real problem is that C++ didn't originally have an exception 
hierarchy.
Now it does.  This is really a legacy code problem, for code not written
to the standard exception hierarchy.

    If you catch "Exception", you catch all the standard exceptions and 
anything
derived from them.   User exceptions should be derived from Exception.
(Python, incidentally, went through the same evolution, and Python now
deprecates exceptions not derived from Exception. For example, throwing
a string is deprecated in Python.)

    So the important cases today are

     void foo() throw(Exception); // can throw anything derived from 
Exception

and

     void foo() throw();		// cannot throw anything

Those should work right and be enforced.  Other cases are secondary.

I'd argue for "throw(Exception)" being the default, and for full enforcement
at compile time, but I suspect that would be unpopular.  It doesn't fit with
the C++ approach of "the least safe option is the default".

					John Nagle

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
John
2/23/2008 12:13:27 AM
Reply: