[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] PATCH: use std::uncaught_exceptions()

From: Greg Chicares
Subject: Re: [lmi] PATCH: use std::uncaught_exceptions()
Date: Sat, 24 Mar 2018 21:19:34 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.6.0

On 2018-03-24 14:36, Vadim Zeitlin wrote:
> On Fri, 23 Mar 2018 22:14:02 +0000 Greg Chicares <address@hidden> wrote:
> GC> In 'handle_exceptions.hpp' I've trivially updated this comment:
> GC> 
> GC> /// It may seem like a good idea to test std::uncaught_exceptions()
> GC> /// right before the try block, as recommended here [these references
> GC> /// speak of the related facility deprecated by C++17]:
> GC> ///   
> http://groups.google.com/group/comp.lang.c++.moderated/msg/ec0ef69dd3949955
> GC> ///   "Before the try-block in report_exception, query
> GC> ///   std::uncaught_exception() to determine if an exception is
> GC> ///   active. If it is not, throw std::logic_error or some exception
> GC> ///   that you know that your framework will catch."
> GC> /// but actually that's invalid--see:
> GC> ///   
> http://groups.google.com/group/comp.lang.c++.moderated/msg/aa7ce713ee90c044
> GC> ///   "The only problem with uncaught_exception is that it doesn't
> GC> ///   tell you when you're in a catch(...) { ... throw; } block"
> GC> 
> GC> but now I wonder: does this C++17 improvement address exactly the
> GC> problem described in the second clc++m citation above?
>  I admit I haven't reread all this long thread from 1999, but I don't think
> std::uncaught_exceptions() can help with the problem here, at least if I
> understand it correctly. And my understanding, gathered from looking at the
> commits c31c664, 40d9427 and df4fc26 is that we'd like to check whether
> report_exception() is called from a catch clause, in order to prevent the
> "throw;" statement in it from invoking std::terminate(). However neither
> std::uncaught_exception() nor std::uncaught_exceptions() can be used for
> this because both would return 0 when called from a "catch" clause after
> throwing a (single) exception, as they only count "live" exceptions and not
> the ones being handled.

Yes, after rereading all of this, along with
I think you're saying exactly what Abrahams was saying in the second
clc++m citation above: that uncaught_exception[s]() can't tell us
whether our handler was called from a catch-clause.

> GC> And if so, should we change 'handle_exceptions.hpp'
>  So the only change I'd consider would be to explain more clearly in the
> comment that we can't use std::uncaught_exception[s]() here, without any
> references to the external sources as the real reason for it is very
> simple: if we used it, we'd never do anything in this function at all as
> uncaught_exception{,s}() would always return {false,0}.

I still find that mistaken notion--that uncaught_exceptions() might
tell me whether I'm in a catch-clause--seductive. I understand what
you and Abrahams are saying, but I could fall into that trap again,
so I couldn't bring myself to purge the old thesis and antithesis.
But I've cited your post in that comment and quoted the last line
above. I call this the Law of the Affirmation of the Negation.

> GC> I think the last time I looked at std::uncaught_exception[s]() was
> GC> over a decade ago, and I concluded then that it wasn't actually
> GC> very useful--but I see you've used it in code written since that
> GC> time, so you must have a better understanding of it than I do.
>  std::uncaught_exception() is useful, but limited, which does make it not
> very useful in practice and std::uncaught_exceptions() removes the
> limitation making it very useful indeed. However both of them can only be
> used from object destructors in a useful way, to the best of my knowledge.
> There they can be used in order to execute cleanup action on scope exit
> depending on whether the scope is being left normally (without exception)
> or abnormally (due to an exception being thrown). E.g. they allow
> implementing Alexandrescu's scope guard class without having an explicit
> dismiss() method to prevent it from doing its cleanup on normal exit, so
> that it becomes possible to easily define a "rollback" class that only
> performs the rollback in its dtor if an exception happened.

Examining every use case in lmi:
  vim -p $(grep --files-with-match uncaught_exception *.?pp)
I see no rollback semantics--not surprising because we have no "undo".
We use std::uncaught_exceptions() only for diagnostics.

In 'progress_meter.hpp', I modified the commentary to point out that
we might have used std::uncaught_exceptions() instead of culminate(),
if we were designing this today. But we already call culminate()
exactly where necessary, so there's no real need to change this now.
The advantage of using a dtor instead is that you can't omit it by
accident or write it in the wrong place.

There are a couple of occurrences in the wx_test code, which isn't
part of the production system distributed to end users. wx_test is
a canary in a coal mine: its purpose is to fail unmistakably when
anything is wrong, and std::uncaught_exceptions() just helps it
fail cleanly.

The only other use is in 'ledger_pdf_generator_wx.cpp', and I'm
not sure it does exactly what we want there:

    ~numbered_page() override
        // Check that next_page() was called the expected number of times,
        // unless we're unwinding the stack due to some other error, in which
        // case it is normal that extra pages haven't been generated.
        // Notice that we shouldn't use LMI_ASSERT() in the dtor by default,
        // and it's better to use warning() instead of using noexcept(false).
        if(extra_pages_ && !std::uncaught_exceptions())

If an exception has been thrown, then of course we don't need to
warn that it prevented PDF generation from completing. However,
if the page count is wrong but no exception has been thrown,
then I'd say that a crucial postcondition has been violated, and
displaying a warning message doesn't seem severe enough: I'd
rather delete the PDF file in that case, or, much better, prevent
it (or even any fragment of it) from being written to disk. That's
lmi's sine qua non: never write an invalid PDF file, because ours
is a heavily regulated industry (and page numbering actually is
prescribed by regulation). How should we implement that?
 - throw in the dtor when not unwinding, even though that goes
   against accepted pre-C++17 wisdom?
 - make correct page numbering a postcondition of
    - standard_page
    - page_with_tabular_report
   and (fragilely) any other derived class added later?
 - something else?

reply via email to

[Prev in Thread] Current Thread [Next in Thread]