Boost logo

Boost :

Subject: Re: [boost] [outcome] Ternary logic -- need an example
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2017-05-19 13:42:29


> Ok, I remember, both error_code and exception_ptr are
> default-constructible. It is not immediately clear to me that a
> default-constructed error_code represents a no-error condition.

A null error code is widely held to mean "no error". Ditto with
exception_ptr. In any ASIO completion handler, you'll see:

void asio_handler(const error_code &ec, ...)
{
  if(ec)
    handle_error();
  ...
}

> In the blog
> post by Chris Kohlhoff you refer to, he uses the following enum for
> representing http error conditions:
>
> ```
> enum class http_error
> {
> continue_request = 100,
> switching_protocols = 101,
> ok = 200,
> ...
> gateway_timeout = 504,
> version_not_supported = 505
> };
> ```
> Which implies that numeric value 200 means no-error and a value initialized
> `http_error` is meaningless. Maybe this does not affect the value of a
> default-constructed `std::error_code`, but surely it adds to the confusion.

I have absolutely no idea why Chris chose "http_error" for that enum. It
should have been "http_status" because those are the HTTP status codes
as per https://en.wikipedia.org/wiki/List_of_HTTP_status_codes.

I am also highly unsure why you'd choose the error_code infrastructure
for these. It's not like almost all of the HTTP status codes (e.g. 306
Switch Proxy) can be any error condition except
resource_temporarily_unavailable.

Vinnie, what does Beast do?

>> Therefore calling o.error() on a valued outcome returning a default
>> constructed (null) error code is exactly correct: we return there is no
>> error.
>
>
> Only under some definition of "correct". By correct, you probably mean "not
> invoking UB" and "being compliant with your specification", but it is not
> intuitive at all that a function should return this or that when invoked in
> a context that is most likely a programmer's bug.

No, really a null error code meaning "no error here" is the widely held
interpretation. The reason I cannot categorically says it means no error
here is because the C++ standard guarantees that a null error code has
value 0 and the **system** category. And that's a defect, I think they
meant value 0 and the *generic* category, because if they had then the
standard would categorically guarantee a null error code means no error
here.

As it currently stands, the standard accidentally has made a null error
code mean "system dependent behaviour" which if it didn't mean "no error
here" would fundamentally wreck the Networking TS and the Filesystem TS.
Yay.

>> No undefined behaviour needed, and you can write your code using
>> Outcome with the hard assumption that o.error() will always return an
>> accurate view of the current state. No need to check .has_error(), or
>> anything like it.
>
> Modulo this situation with `http_error::ok == 200`. But with this you are
> also saying, the library provides two ways for checking if you have an
> error:
>
> o.has_error(); // option 1

This returns whether the outcome contains an error_code. Not whether
there is an error. A program might return an outcome containing a null
error_code. It probably is a bug, but Outcome can't reasonably enforce
how people misuse error_code, not least because of the C++ standard
defect above.

> o.error() == std::error_code{}; // option 2

Actually "!o.error()" but I think you meant to write that anyway.

This would be preferred over option 1. If the function returned an empty
outcome, this would throw, but that is probably a good thing.

> And by describing clear semantics for option 2, you are saying, it is
> equally fine to use option 2 for checking if we have an error. This
> encourages the usage of option 2, but I would not want my colleague
> programmers to start using this syntax, because I then cannot tell proper
> usages from inadvertent omissions. And reading the value of `error()`
> without having confirmed that some error occurred is almost surely a bug,
> even if you can assign a well defined semantics in `boost::outcome` for it.

Option 1 is misleading. Option 2 is correct, and means typing less
boilerplate e.g. if(o.error()) ... instead of if(o.has_error() &&
o.error()) ...

> I am fine with this scope disabled. However, `tribool` is still in scope,
> so I consider it valid to ask about its usefulness (especially given that
> all contexts in which it would prove useful is disabled). Maybe `tribool`
> should be also removed from the scope?

I believe you've convinced me to move tribool to under the advanced
operations macro. Thanks. Logged to
https://github.com/ned14/boost.outcome/issues/22

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

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