2017-06-12 23:26 GMT+02:00 Emil Dotchevski via Boost
On Mon, Jun 12, 2017 at 1:50 PM, Andrzej Krzemienski via Boost < boost@lists.boost.org> wrote:
2017-06-12 22:28 GMT+02:00 Emil Dotchevski via Boost < boost@lists.boost.org> :
On Mon, Jun 12, 2017 at 1:15 PM, Andrzej Krzemienski via Boost < boost@lists.boost.org> wrote:
Can you post an actual example so we're not talking in the abstract?
``` int job(int x) { int y = f(x); // f might fail, but I forgot int z = g(y); // g might fail, but I forgot return h(z); } ```
If for some reason, I have forgotten that f() might fail (and signal failure), does function g() get called? In case of exceptions no, because if f() throws, then g() is never called. In case of outcome<>: no, because f() returns `outcome<int>, so the above will fail to compile, an I will be forced to rewrite function job().
If you choose to write this code, in Noexcept you will get an assert. Even in NDEBUG builds,
At which point? Where is the assertion located?
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. When I write function `job` (from the example above), I should make sure that it does not happen. But I might simply forget about it for some reason. The question now is: does the framework help me if I forget? In case of C++ exceptions, the answer is "yes": the call to `g()` is automatically skipped if I forget. In case of outcome<>, the answer is "yes": the compiler refuses to compile the code. 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?
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. C++ exceptions can both prevent invalid invocations of `g()` and remain exception-neutral. This is fine. `outcome<>` prevents invalid invocations of `g()` but compromises exception neutrality (still fine by some standards, and sometimes desirable). In case of Noexcept, you provide neutrality, but compromise the guarantee that invalid invocations of `g()`are prevented. This might be considered a wrong trade-off. Regards, &rzej;