|
Boost : |
Subject: Re: [boost] [review] Review of Outcome v2 (Fri-19-Jan to Sun-28-Jan, 2018)
From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2018-02-01 13:34:38
On 02/01/18 16:32, Andrey Semashev wrote:
> On 02/01/18 15:03, Jonathan Müller via Boost wrote:
>> On 01.02.2018 12:10, Andrey Semashev via Boost wrote:
>>>
>>> The important difference between using exceptions and error codes (or
>>> Boost.Outcome, I presume) is that in case of exceptions the user has
>>> to make an effort to write broken code and the correct code most of
>>> the time comes naturally, while with manual error checking it is the
>>> other way around. This is the reason why manual error checking is
>>> more prone to mistakes in error handling.
>>>
>>> PS: All that, of course, is given that RAII is ubiquitous. If it's
>>> not then error handling is difficult regardless of the tool you use.
>>
>> This entire mistake would have been prevented, if a proper return type
>> was used, for example a sane optional (that doesn't try to be a pointer):
>>
>> static optional<A> A::create(â¦) {â¦}
>>
>> Then, this will not compile:
>>
>> auto a = A::create(â¦);
>> a.foo(); // error!
>>
>> So, you're forced to write:
>>
>> a.value().foo();
>>
>> And accessing the value on an optional without checking it should
>> always be a red flag and not just written "naturally" (That's why
>> operator-> for optional is a mistake IMO).
>
> I disagree. The necessity to write ".value()" on every access to `a` is
> an overhead, both in user's effort and runtime performance. This,
> actually, illustrates my point.
>
> (And no, `operator->` is the most often used way to access the value
> stored in an optional in my code, as I'm sure it is in that of many
> other people. Test for presence once, use unchecked afterwards.)
>
>> Yes, it's still prune to mistakes, but so are exceptions, it's just a
>> little bit more work:
>>
>> Foo::Foo()
>> {
>> Â Â Â try
>> Â Â Â {
>>        my_a = A::create(â¦);
>> Â Â Â }
>>    catch (â¦)
>> Â Â Â {
>> Â Â Â Â Â Â Â log_error();
>> Â Â Â }
>> }
>
> Thing is, you don't usually write that code. You usually catch
> exceptions at the point where you can do something about them, which in
> this case is the client code that requested an object of `Foo`. So your
> typical code would be this:
>
> Â Foo::Foo() : my_a(A::create())
> Â {
> Â }
>
> Â void Foo::bar()
> Â {
> Â Â Â // my_a is guaranteed to be valid
> Â }
>
> Â try
> Â {
> Â Â Â auto pFoo = std::make_unique< Foo >();
> Â Â Â pFoo->bar(); // pFoo is guaranteed to be valid
> Â }
> Â catch (...)
> Â {
> Â Â Â // handle errors
> Â }
>
> Whereas, with manual error handling, all code between the error in
> `A::create` and the actual error handler and also `Foo::bar` etc.
> becomes "tainted" with checks.
Which is much more opportunity to make a mistake.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk