From -3161799368343466402
X-Google-Thread: f78e5,82cd3599ce330676
X-Google-NewGroupId: yes
X-Google-Attributes: gid7894ca11fe,domainid0,public,usenet
X-Google-Language: ENGLISH,ASCII-7-bit
Path: g2news2.google.com!news1.google.com!npeer02.iad.highwinds-media.com!news.highwinds-media.com!feed-me.highwinds-media.com!news.alt.net!frodo.cs.rpi.edu!not-for-mail
From: Kazutoshi Satoda <k_satoda@f2.dion.ne.jp>
Newsgroups: comp.std.c++
Subject: Re: throwing in a noexcept(true) function
Date: Thu,  8 Apr 2010 18:25:47 CST
Organization: unknown
Lines: 203
Sender: cppmods@cs.rpi.edu
Approved: james.dennett@gmail.com
Message-ID: <4BBE39E5.5000409@f2.dion.ne.jp>
NNTP-Posting-Host: netlab.cs.rpi.edu
Content-Type: text/plain; charset=UTF-8; format=flowed
To: (Usenet)
Return-Path: <cppmods@ruralroute.cs.rpi.edu>
X-Original-Date: Fri, 09 Apr 2010 05:17:41 +0900
X-Submission-Address: std-c++@netlab.cs.rpi.edu
Xref: g2news2.google.com comp.std.c++:2405

Herb Sutter wrote:
>
> It's true that Yes/No is a much simpler subset of SetOfTypes/None. But
> it's still the same principle. The question is whether the great
> reduction of possibilities makes the problem tractable.

After long thoughts on writing a reply to your post, I have realized a
way how noexcept can reduce the overhead of throw().

Relaxing the rule of stack unwinding before termination can reduce the
number of stack unwinding path, which depends on the number of objects
having non-trivial destructor in the case of throw(), into only one
which goes to std::terminate(). Right?

My emphasis on static checking was all based on such idea that static
checking is the only way to reduce the overhead of mandated runtime
behavior to acceptable level. But after realizing the above, I
understand that static checking for noexcept specification is not so
important while having some difficulties.


However, I'm still looking forward about some kind of static checking of
binary noexcept. It may be, noexcept clause in a function; noexcept: ...
or noexcept { ... } as we often write a comment on a few final lines of
a function like "never throws here". But this is surely beyond the
upcoming C++0x.

The following includes some answers for your questions and also includes
some questions about your assertions about static checking.

> It seems to me that there are some fundamental things here:
>
>  1. Static checking inherently defeats writing naturally
> exception-neutral code which should be the default, because the
> specifications have to be written explicitly to be useful (and this is
> true whether the default is "throws nothing" or "throws anything").

What would be required to be written explicitly to make intermediate
call-chains exception-neutral?

   void maythrow();
   void neverthrows() noexcept;
       void intermediate()
   {
       maythrow();
       neverthrows();
   }
       void catcher()
   {
       try
       {
           intermediate();
       }
       catch ...
   }

Here, I believe intermediate() is exception-neutral without any noise;
doesn't care about any exceptions and their circumstances to be thrown,
and propagates them as they are.

>  2. Static checking is intrusive and viral from the bottom up and has
> to be explicitly written to be useful (again, whether the default is
> "throws nothing" or "throws anything").

In the above code example again, I can't see any viral effect caused by
the bottom function neverthrows() to callers.

I understand the viral effect of Java's exception specification is that
if maythrow() have a "throw Exception" specification, intermediate() or
catcher() or both are affected. I agree that this is untenable and one
of the reason of unfortunate of Java's static checking, but static
checking for noexcept won't cause such a viral effects.

If you are saying about the strong demands on callees of neverthrows()
to have noexcept, I agree that is a problem. Such strong demands may
cause premature decision to specify noexcept, which can likely cause
versioning problems later.

>  3. Noexcept used "for correctness" is for only a very small set of
> code, and its new use of "for performance" applies to general purpose
> code but has to be very simple to write if it's to be used there.

I agree on this.

> In
> particular, unlike today's exception specifications, including even
> throw(), noexcept allows optimization of the noexcept function's
> callees (functions called by the noexcept function, for example to
> assume those must be noexcept as well if they are not called within a
> try/catch), not just its callers.

This part was the key of my realization about overhead reduction. Thank
you very much.

However, I think it was good to mention that the "callee" in effect are
restricted to those which are inlined or local in the same compile unit
in general. It was hard to imagine how the caller can affect to
optimization of a callee which can be in another compile unit. Please
let me know if I'm wrong here.

>> template <class T, class Allocator = allocator<T> >
>> class vector {
>>  ...
>>  iterator erase(const_iterator position) noexcept(
>>      has_nothrow_copy_constructor<T>::value &&
>>      has_nothrow_move_constructor<T>::value &&
>>      has_nothrow_copy_assign<T>::value &&
>>      has_nothrow_move_assign<T>::value)
>
> This seems problematic for several reasons, mostly on the grounds of
> usability:
>
>  - Usability: To me, this falls squarely into "the cure is worse than
> the disease" territory.

Now I can agree this evaluation.

>  - Usability: Requiring static checking means you couldn't
> instantiate vector<T> and call erase with any type T that doesn't
> write noexcept on its user-written special functions, right?

Right, if it is in a noexcept function.

>  - Usability: What about more general template functions that would
> call named member functions, operators, etc. on the type T? Are we
> going to have to correctly list every possible member function that
> might be called? What about if it's an overloaded member function?

The operator noexcept will allow such specification, like this:

   template<typename T>
   void f(T& x) noexcept(noexcept(x.foo(), x.foo(0), ++x));

Listing of expressions are not more difficult than listing conditions at
the "Throws:" clause in the documentation, as far as the condition can
be written as a constant expression like noexcept expression. I found
that operations on iostreams involves a runtime property,
basic_ios::exceptions(), to determine it may throw or not.

>  - Generality/readiness: Has someone tried this system with complex
> examples to see how it deals with issues like the above, such as
> overloading?

No.

But I have a similar suspect on the current draft: Can noexcept solve
the problem which happened on throw()? Will Boost adopt noexcept if it
dropped support of C++2003 compilers?

Boost concluded not to use throw() because it can cause pessimizations
on a "dumb" compiler. I'm afraid of such compilers to continue causing
pessimizations for noexcept, too.

> Also, for this specific example, I think you would want to include
> requiring a nothrow destructor too, right? How easy would it really be
> to write these specifications correctly?

Although throwing destructor is prohibited as violation of Destructible
requirement on value_type, it also can be specified with operator
noexcept with explicit destructor call expression (x.~T()).

> if you require static checking, you are requiring all code that
> can be called from a checked function must state its throws (Java) or
> noexcept (your proposal for C++) clause fully and correctly, right?

Probably right, but it sounds a kind of begging the question.

I thought that explicit try could be used as an escape. But, yes, it
means defeating the check, of course.

>> Programmers won't be forced to write exception specifications

(snip)
>>
>> until they
>> write static exception specification for their own function,
>
> .... which they cannot do until one is already written on every
> function they need to call, because it's viral down. Unless they want
> to work around it by writing unnecessary try/catches which would add
> code clutter and can add some overhead.

I thought such explicit try might be a good sign of possibility of
std::terminate() and inefficiency, which will be silently introduced.

> The only way I can see this is significantly better
> than Java is that when you just give up on wanting to get the benefit,
> instead of writing "throws Exception" everywhere as in Java you would
> get to just stop writing the specifications because the default is
> "throws something" -- but that isn't an argument in favor of static
> checking because it means punting and not using the feature.

I still think this can be an argument to re-evaluate static checking on
C++ separately, although the evaluation resulted in not taken for now.

--
k_satoda

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]



