Boost logo

Boost :

Subject: Re: [boost] expected/result/etc
From: Emil Dotchevski (emildotchevski_at_[hidden])
Date: 2016-02-10 18:23:33


On Wed, Feb 10, 2016 at 1:30 PM, Gavin Lambert <gavinl_at_[hidden]>
wrote:

> On 11/02/2016 08:40, Emil Dotchevski wrote:
>
>> It doesn't matter what your use case is: while the success/failure
>> vocabulary isn't incorrect, it is more precise to think in terms of
>> postconditions. You call a function, it will either satisfy its
>> postconditions or it won't return. Sure, you can and should return all
>> information that the caller needs in order to proceed with whatever
>> operation is being attempted, but there is no need to return anything if
>> the operation can't proceed.
>>
>> So all you need to do is correctly identify the conditions that indicate
>> that the operation can't proceed, and throw an exception.
>>
>
> That's still a fuzzy line though. In the delete file case, the operation
> of deleting the file cannot proceed because the file is already absent.
> However the post-condition of "the file no longer exists" is still met. Is
> that success or failure?
>

"Proceed", I mean the caller. Let's say you have code which opens the file
then reads from it. The caller can not proceed to reading if the file
couldn't be opened. So, the postcondition of the open operation is that the
file was successfully opened. Similarly, the postcondition of delete_file
is that the file does not exist, because presumably the caller of
delete_file can't proceed if the file still exists.

> There are other cases where an operation might fail, or (if you prefer
> that term) be unable to proceed (eg. "queue is full, cannot push new
> item"), but throwing an exception in this case is not a good design choice
> unless you *know* that the queue is never supposed to be full -- which is
> not something that the queue itself can know unless it's a supposedly
> unbounded queue and so the only way it can be full is if it cannot allocate
> more memory.
>

If you know that the queue is never supposed to be full, that is, if the
full queue indicates a bug in your code, then you should assert rather than
throw. Throwing is when you expect the program to successfully recover from
an anticipated (by the programmer) failure.

> But for bounded queues, the library generally has to assume that it could
> reach the bound at some point and have to refuse to add new items, so that
> should be a status return rather than an exception.

It depends. For example, if this is a keyboard buffer queue, then probably
you'd return a status to indicate that there's no more space, and the
caller will "beep" to tell the user to slow down with the typing. In this
case it would be annoying for the caller to have to catch an exception. On
the other hand, if it's a job queue, where the caller expects all submitted
jobs to be queued and at some point completed, then an exception is more
appropriate, so the immediate caller wouldn't have to care (push won't
return), while some context higher up the call stack can perhaps retry the
whole sequence of jobs, or notify the user that the operation failed.

We can't discuss this stuff in the abstract. Defining correct
postconditions depends very much on the specific API being designed.

> And yet the application could choose to use such a queue with a really
> large bound or with a producer that's massively slower than the consumer,
> and so the assumptions the library made about the balance of success vs.
> failure weren't correct.

In this case either we have a user error or a library design error. You
have to get the postconditions right, or else the users will end up having
to deal with exceptions in what they consider non-failure situations, or
end up writing (and forgetting to write) if( error ) return error. I
personally have better things to do. :)

Emil


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