Boost logo

Boost :

Subject: Re: [boost] Noexcept
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-06-14 07:47:11


2017-06-13 23:01 GMT+02:00 Emil Dotchevski via Boost <boost_at_[hidden]>
:

> On Tue, Jun 13, 2017 at 12:25 AM, Andrzej Krzemienski via Boost <
> boost_at_[hidden]> wrote:
>
> > > let's not forget that if f() fails, y is invalid and g()
> > > should reject it (think of it as e.g. fread getting a 0 for its FILE *
> > > parameter).
> >
> > I absolutely agree that it is wrong to pass an invalid `y` to `g()`, I
> > think we are just disagreeing on who is responsible for preventing this
> > from happening.
> >
>
> I think that I don't disagree at all, but I am pointing out that 1) no
> matter what, g() *should* assert on its preconditions anyway, and 2) if
> there is a danger for g() to silently not fail when given bad data, then
> the choice of return type for f() is poor. For example, usually I'm not
> terribly worried of the following bug escaping undetected:
>
> shared_ptr<foo> f();
> void g( foo & );
> ....
> g(*f()); //bug, no error check
>
> That's because dereferencing an empty shared_ptr fails dramatically, in
> fact (if exceptions are disabled) just as dramatically as if I had:
>
> result<shared_ptr<foo> > f();
> void g( foo & );
> ....
> g(*f().value() ); //.value checks for errors
>
> You say that in case of Noexcept, there is an assertion, but I fail to see
> > where you could put this assertion, so that a call to `g()` is prevented.
> > Maybe you could elaborate?
> >
>
> Misunderstanding. The call to g() is not prevented, what's prevented is the
> attempt of g() to call throw_ if the error from f() wasn't handled.
>

Ok.

>
> But again, I agree with all of your concerns, it's just that they're
> (mostly, not 100%) orthogonal to what Noexcept lets you do. If you feel
> that you want to put a FILE * into some result<T>, more power to you (do
> note that since in Noexcept that type doesn't have to transport errors, it
> can be implemented in 2 lines in terms of optional<T>).
>
>
> > > Perhaps your point is that ideally f() shouldn't return int but a
> > different
> > > type with special semantics. Okay.
> > >
> > > OTOH let's say you're returning a file descriptor. I'd think that
> > > optional<int> is an overkill in this case, in fact you're probably
> > > obfuscating the fact that the int is a FD -- what does it mean if you
> get
> > > -1 FD in an optional<int> (or for that matter in an outcome<int>)? It's
> > > redundant, and after all it's a safe assumption that you won't get
> silent
> > > failures from functions if you pass -1 for the FD.
> >
> > A valid concern. A solution to that has been provided by Vicente: just
> use
> > type `uncertain<T>`, which is just a `T` inside, but because it is a
> > different type, you cannot silently use it in place of `T`. But this
> would
> > compromise your other goal: that some functions want to remain
> > exception-neutral.
> >
>
> How does it compromise it? This uncertain<T> must still have an invalid
> state, a value that is returned when there is an error. If that value is
> simply uncertain<T>() (which, obviously, it is), you can just return
> throw_(my_error()) from a neutral function that returns uncertain<T>.
>

Just to clarify: when I say `uncertain<T>`, I mean something like:

template <typename T>
struct uncertain
{
  uncertain(T&& v) : value(std::move(v)) {}
  T value;
};

It cannot hold any special value that would signal an error. It is more
like an opaque typedef on T. Its only purpose is to signal a compile-time
error when I obliviously type:

auto y = f(x); // returns uncertain<Y>
g(x); // error: cannot convert from uncertain<Y> to Y

And now you have to decide: either apply your `try_` or some invented
`ignore_`.

Regards,
&rzej;


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