2017-06-13 23:01 GMT+02:00 Emil Dotchevski via Boost
On Tue, Jun 13, 2017 at 12:25 AM, Andrzej Krzemienski via Boost < boost@lists.boost.org> 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
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;