C++ Logo

std-proposals

Advanced search

Re: Middle ground between "return" and exceptions?

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Mon, 14 Sep 2020 16:50:05 -0400
On Mon, Sep 14, 2020 at 3:05 PM Barry Revzin via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
>
>
> On Mon, Sep 14, 2020 at 1:44 PM Dmitry Dmitry via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>> TL;DR: What do you think about creating a new flavour of control-flow statements (in particular "return") that could return from outer/parent scope?
>>
>> In the thread "Initialisers in ternary operators" we had this example:
>> std::optional<std::string> FindUsersCity(bool non_default) {
>> std::optional<ContactsServer> contacts = GetOrOpenContactsServerConnection();
>> if(!contacts)
>> return std::nullopt;
>> std::optional<UserId> uid = contacts->GetUserId();
>> if(!uid)
>> return std::nullopt;
>> std::optional<GeoServer> geo = GetOrOpenGeoServerConnection();
>> if(!geo)
>> return std::nullopt;
>> std::optional<Location> uloc = geo->GetLocation(*uid);
>> if(!uloc)
>> return std::nullopt;
>> return uloc->GetCityName();
>> }
>>
>> It seems, that there are many situations with the following pattern:
>>
>> there is a type
>> that defines some invalid state (in case of iterator the state is "being equal to end()", in case of Result/Outcome/Expected the state is "representing an error", in case of optional it is "nullopt", and so on...)
>> attempts of using which in the invalid state are supposed to "return" (from the scope where it is used). We can take a step back and consider other control-flow statements.
>>
>>
>> Without going into implementation details, what do you think about the idea (on a conceptual level) of being able to describe this pattern somewhere somehow?
>>
>> If conceptually it is fine, then what do you think about implementing the idea by creating another flavour of return statements that can return not only from its own scope, but also from outer/parent scope?
>> If you wish, this new flavour can be viewed as a middle ground between (1) regular return's (which often requires boilerplate for handling the pattern above) and (2) exceptions (which are too non-local and implicit and can "jump" at an arbitrary place).
>>
>> The code above would look like this:
>> std::optional<std::string> FindUsersCity() {
>> std::optional<ContactsServer> contacts = GetOrOpenContactsServerConnection();
>> std::optional<UserId> uid = contacts.GetOrReturnNullOpt()->GetUserId();
>> std::optional<GeoServer> geo = GetOrOpenGeoServerConnection();
>> std::optional<Location> uloc = geo.GetOrReturnNullOpt()->GetLocation(*uid);
>> return uloc.GetOrReturnNullOpt()->GetCityName();
>> }
>> where GetOrReturnNullOpt() is a method that (1) either returns a value if std::optional is not empty, (2) or returns from FindUsersCity() if std::optional is empty.
>>
>>
>> I think it could solve
>>
>> Initial problem mentioned in "Initialisers in ternary operators": we could have something like this: return Cont.find(42).GetOrReturn(0).
>> The problem of using optional without boilerplate (see above).
>> The problem of Error-handling via Result<> without boilerplate, which has been a constant source of pain and complaints and holy wars between exceptions and return-values for error handling.
>> It also eliminates one more reason to exist for macros (that is how people are solving it right now: 1, 2).
>>
>>
>> Any thoughts?
>>
>>
>> --
>> Dmitry
>
>
> As I said in https://lists.isocpp.org/std-proposals/2020/09/1811.php, this already exists in C++20 in the form of coroutines...

We *really* don't want to abuse coroutines for that. Making a function
a coroutine causes fundamental changes that alter how that function
gets to interact with the rest of the world. For example, coroutines
can't participate in guaranteed elision (or even non-guaranteed
elision). Also, it forces you to use `co_return` to return, just
because you wanted to exit elsewhere.

And adding coroutine machinery to a type requires expert-level
understanding, far beyond the needs of just writing an `optional`-like
type. That shouldn't be required for doing something like this.

Now, that being said, I think the idea is not a good one. If we had
light-weight exceptions, we wouldn't need to use `optional` for cases
like this or ValueOrError types like these at all. We could just write
reasonable code and let a catch block handle the rest.

Received on 2020-09-14 15:50:17