Boost logo

Boost :

Subject: Re: [boost] Noexcept
From: Emil Dotchevski (emildotchevski_at_[hidden])
Date: 2017-06-14 00:29:15


On Tue, Jun 13, 2017 at 4:33 PM, Gavin Lambert via Boost <
boost_at_[hidden]> wrote:

> On 13/06/2017 21:39, Emil Dotchevski wrote:
>
>> I can't imagine a function which returns shared_ptr<T> *and* which
>> could fail, to return an empty one in case of success. That said, in
>> that case all it means is that you can't use throw_ directly in a return
>> expression; or you could partially specialize throw_return<> for a specific
>> type of shared_ptr, if that's appropriate.
>>
> private:
> void create_X_if_needed() noexcept
> {
> if (!m_X)
> {
> try
> {
> m_X = make_shared<X>();
> }
> catch (internal_error const& e)
> {
> throw_(e.code());
> }
> }
> }
>
> public:
> shared_ptr<X> get_X_if_enabled() noexcept
> {
> if (feature_X_enabled())
> {
> create_X_if_needed();
> }
> return m_X;
> }
>
> get_X_if_enabled() could return an empty pointer in two cases:
>
> 1. If feature_X_enabled() returns false during all calls, this is a
> successful return with no pointer.
> 2. If X's constructor throws an internal_error, this is an error code
> return with no pointer.
>
> Regardless, the correct way to check for errors is to see if the result is
>> valid. This is the price one must pay when not using exception handling.
>>
>
> Isn't the correct way to check for errors (without explicitly catching
> errors) in this case to call has_current_error()?
>

Gavin, thank you for this question. Yes, in this case has_current_error()
would be correct, since the return value itself can't communicate success
or failure.

> Granted in this case get_X_if_enabled doesn't actually need to explicitly
> check for errors since the following statements will work regardless of
> whether there was an error state or not.

That seems wrong to me. It's difficult to reason about something like this
in the abstract, but presumably failing to create_X is a Big Problem while
the feature_X being disabled is not. If I've enabled feature_X, I probably
don't want it to fail to work silently.

> But say if it wanted to do some logging only in the case where
> create_X_if_needed didn't fail, that would have to be written something
> like this:
>
> shared_ptr<X> get_X_if_enabled() noexcept
> {
> if (feature_X_enabled())
> {
> create_X_if_needed();
> if (!has_current_error())
> {
> log("has X");
> }
> }
> return m_X;
> }
>
> The other possibility is to catch and rethrow the error, but that seems
> more cumbersome and error-prone.
>

I think it's not cumbersome at all. Just like when using exceptions, with
Noexcept the ability to catch_, do_some_work then throw_ is an important
feature. Consider that in general in this context you might not have the
slightest idea what errors may pass through it; so you'd:

if( auto tr=try_(....) ) {
  //ok good, do work then return a "good" value
} else {
  log(BOOST_DIAGNOSTIC_INFORMATION(*tr.catch_<>()));
  return throw_();
}

Except it won't work, you've uncovered an omission in the Noexcept API. The
problem is that catch_<> will flag the error as handled, and in this case
you don't want that. I need to add another function similar to catch_<>
which only gets the error without handling it. Not sure what to call it, or
maybe instead I can add a member function tr.throw_() which flags the error
as unhandled.

> (Related: if the original throw_ was a custom type not derived from
> std::exception, and catch<> is used to catch it as a std::exception and
> throw_ it again, can it still be caught later with a catch_<custom_error>?
> Does the answer depend on whether throw_() or throw_(e) was used?)

throw_() is a noop, except that it converts to anything for return
throw_(), returning throw_return<R>::value() where R is the return type of
the function.

throw_(my_error()) will inject std::exception as a base if my_error doesn't
already derive from it.


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