2017-06-14 2:29 GMT+02:00 Emil Dotchevski via Boost
On Tue, Jun 13, 2017 at 4:33 PM, Gavin Lambert via Boost < boost@lists.boost.org> 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.
Yes. this would make Noexcept superior to C++ exceptions in this aspect: this would allow to easily identify when you are handling the exception and when you are just modifying the exception. Call it "augment"? Regards, &rzej;