Boost logo

Boost :

Subject: Re: [boost] [contract] noexcept and throwing handlers
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2016-08-10 11:06:29


On Sun, Aug 7, 2016 at 5:22 PM, Gavin Lambert <gavinl_at_[hidden]> wrote:
> On 8/08/2016 01:06, Lorenzo Caminiti wrote:
>>
>> void fclose(file& f) noexcept {
>> if (!f.is_open()) preconfition_failure_handler(from_function);
>> ...
>>
>> Where by default the handler terminates:
>>
>> precondition_failure_handler = [] (from) { std::terminate(); };
>>
>> But programmers can redefine it to throw (as you suggested above, but
>> beware of what N4160 points out plus on how to program a throwing
>> entry_invariant_failure_handler that shall not throw when from ==
>> from_destructor):
>>
>> precondition_failure_handler = [] (from) { throw
>> failed_precondition(); };
>>
>> Or to log and exit:
>>
>> precondition_failure_handler = [] (from) { some-logging-code;
>> exit(-1); };
>>
>> Or to take any other action programmers wish to take on precondition
>> failure.
>
> It is an error for a noexcept function to call a non-noexcept function
> outside of a try-catch block.

I guess you could argue this is a programming error... in any case,
the standard just says that std::terminate() will be called as the
exception tries to escape the noexcept function.

> It therefore follows that if
> precondition_failure_handler is callable in that manner, it must be
> noexcept, and therefore cannot throw. Otherwise something is fundamentally
> broken.

The precondition (or any other contract) failure will always result in
a call to std::terminate() by the noexcept function even if the
precondition_failure_handler (or any other contract handler) is
programmed to throw, that's all.

> Whether the caller is a destructor or not is irrelevant; this applies to any
> noexcept method.

True. I have used destructors just because they are a well known
example of functions that should (almost always) be noexcept. The same
reasoning applies to any other noexcept function.

> Any code that makes special cases for destructors (other
> than to treat them as noexcept even if not specified) is probably also
> erroneous.

Yes, in fact you can say the following code is making sure to treat
destructors noexcept even when they are not explicit specified
noexcept (i.e., making sure destructors (noexcept or not) do not throw
on contract failures even when the contract failure handlers are
configure to throw exceptions for contracts that fail from functions
other than destructors):

    entry_invariant_failure_handler = [] (from where) {
        if(where != from_destructor) throw entry_invariant_failure();
        std::terminate();
    }

Thanks,
--Lorenzo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk