C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::value_or

From: <roberto.romani_at_[hidden]>
Date: Thu, 17 Nov 2022 18:03:48 +0100
Hi Eric

Thank you for your time.

To be clear I am fine to reduce the complexity of value_or and remove the
support for the invocables, my proposal of one year ago was without it, then
people in this mail list said that to have callables was a must. At the
beginning I though "it is going to get too complicated", then implementing
it I was doing some test and I start to think "may be not". But again, no
problem to remove it.
I am fine also to remove the support to the weak_ptr, as you have pointed
out, if you want to pass a weak_ptr to value_or you would need only to call
the lock() method of the weak_ptr.
I think the support of the nullptr does not add too much complexity, but no
big problem to remove it, also because expect for my unit tests, I don't
know how much it can be useful.

About the operator! , it could be substitute with the operator bool, also
std::optional uses it as alternative to has_value().

The arguments pack makes difficult to pass to the value_or "how to test if
there is the value" and "how to get the value", because each argument may
have different rules (actually another thread is "allow multiple parameters
packs of different types" that may be the solution, but other topic).

A possible way to manage special cases could be:

struct record
{
    std::optional<int> v1;
    std::optional<int> v2;
};

data feed, the 1st field of the record is in grams and the second in Kg
std::vector<record> s2
{
    {1000, {}},
    {{}, 2},
    {{}, {}}
 };


Then a class that defines the operators bool and *

class test_and_convert
{
public:
    test_and_convert(const std::optional<int> &v) : _v{ v } {}
    constexpr explicit operator bool() const noexcept { return
_v.has_value(); }
    constexpr int operator*() const noexcept { return *_v * 1000; }
private:
    const std::optional<int>& _v;
};

Then it is possible to do:

std::accumulate(s2.begin(), s2.end(), 0,
        [](int tot, const record& r)
        { return tot +
        s4::value_or(0, r.v1,
            test_and_convert(r.v2)); // with the help of test_and_convert it
converts the second field in grams
        }
    );

In the document of the proposal, I have made an example with strings, a
class that was implementing "if they are empty then they don't have a
value".


"These requirements naturally suggest std::optional..." the idea of value_or
is to return always a value, for this reason there is a default value, like
optional::value_or.
I have in mind also another proposal, not alternative to value_or, but
complementary: something like first_with_value that get a list of objects
that can hold a value (optionals, raw pointers, unique_poiters,
shared_pointers) and it will return the 1st one with a value. The arguments
should be consistent: only optional<int> or unique_ptr<float>.... If no
argument has a value then it returns optional{} or unique_ptr<float>{} or
.... But I am still not sure about the details.

Best Regards
Roberto



-----Original Message-----
From: Std-Proposals <std-proposals-bounces_at_[hidden]> On Behalf Of
Eric Schmidt via Std-Proposals
Sent: Thursday 17 November 2022 07:38
To: Std-Proposals <std-proposals_at_[hidden]>
Cc: Eric Schmidt <eric41293_at_[hidden]>
Subject: Re: [std-proposals] std::value_or

On 11/15/22 12:51 PM, roberto.romani_at_[hidden] wrote:
> Hi All
>
> About the main concern in my proposal "value_or does too much". When I
> was implementing it, I made to myself the same question. Clearly if I
> have sent this proposal I arrived at the conclusion: "no", because
> when I start to implement it, I realized that it is not something so
complicated.
> The weak_pointer is the case that I am not 100% sure, even in the
> definition of the concepts it requires a special part.
>
> @Eric your proposal of value_or is very similar to mine, just removed
> the argument pack and exposing the implementation with the
> contained_value overloadings, if you look my implementation
> (https://github.com/roroberto/cpp_small_simple_stupid_stuff/blob/main/
> value_
> or/value_or/value_or.h) in github you will see that I did something
> very
> similar: all the different cases are managed using simple specialized
> versions of the same function, I just kept the implementation "hidden".
>
> To expose the implementation will have the advantage to allow to
> extend value_or with other special cases, but the side effect is to
> expose the implementation therefore I am not sure about it.

To be clear, I was not suggesting that we allow only one test value. I wrote
it that way because it was simpler and my main intent was to convey the
idea.

My concern with "doing too much" is not really about how complicated it is
to implement, but rather cleanliness of design. The specification of
value_or, as currently proposed, is complex. It would be nice for it not to
know about std::function, etc.

To address your concern about exposing implementation details, I don't think
it is much different from std::sort accepting a comparator, or (to take
something even more closely analogous) the ability to define a swap function
for your class, and thus affect the operation of std::sort.

What I am suggesting is that there is some way for user code to specify

(i) whether a test type contains a value and
(ii) what that value is.

These requirements naturally suggest std::optional, but returning a
std::optional will often require making a copy, which is not always
desirable (or possible). So I was thinking that the return type could be
either std::optional<T> or T*, as appropriate. But as I was writing the
functions I realized the return type could just be anything that supported
operator bool and operator*.

I'm not entirely happy with that way of doing it. Currently I'm not sure
what the best approach is.

(By the way, one thing I find puzzling about your paper is that it uses
operator! to test for value containment. I don't understand the reason for
this.)

> -----Original Message-----
> From: Std-Proposals <std-proposals-bounces_at_[hidden]> On Behalf
> Of Eric Schmidt via Std-Proposals
> Sent: Tuesday 15 November 2022 08:11
> To: Std-Proposals <std-proposals_at_[hidden]>
> Cc: Eric Schmidt <eric41293_at_[hidden]>
> Subject: Re: [std-proposals] std::value_or
>
> On 11/14/22 6:25 PM, Giuseppe D'Angelo via Std-Proposals wrote:
>> Hi,
>>
>> Il 13/11/22 14:27, Roberto R via Std-Proposals ha scritto:
>>> In attachment you can find a draft proposal of a function
>>> std::value_or, similar to std::optional::value_or.
>>>
>>> In previous threads I called it coalesce.
>>>
>>> Can you please tell me your opinion?
>>>
>>
>> I'm very confused by the idea that one allows both "plain values" as
>> well as callables that return values to check.
>>
>> If I call value_or with a bunch of function pointers, trying to find
>> the first non-null pointer (or return the given default), is it going
>> to try to *call* these pointers because they're callables?
>>
>> To me, the "mere" selection facility vs. try to invoke and use the
>> return values ought to be spelled _very_ differently. For instance,
>> for optional there's value_or (that takes a value) and the proposed
>> value_or_else that takes a callable.
>
> I agree with this, and would add that the proposed std::value_or is
> trying to do too much. There are special cases for std::function,
> std::weak_ptr, and std::nullptr_t.
>
> Rather than building lots of cases into std::value_or, I think it
> would be better to have a customization point that determines whether
> there is a contained value, and if so what it is.
>
> I'm thinking along the lines of
>
> template<class T, class U>
> T value_or(T&& default_value, U&& to_test) {
> decltype(auto) value = contained_value(std::forward<U&&>(to_test));
> return value ? *value : std::forward<T&&>(default_value); }
>
> when there is a single test value.
>
> And then we can write functions like
>
> // Generic case
> // Works for optional, pointers, unique_ptr, shared_ptr template<class
> T> T&& contained_value(T&& item) {
> return std::forward<T&&>(item);
> }
>
> // Special cases
>
> int* contained_value(nullptr_t)
> {
> return nullptr;
> }
>
> template<class T>
> std::optional<T> contained_value(const std::weak_ptr<T>& item) {
> auto p = item.lock();
> if (p)
> return *p;
> else
> return std::nullopt;
> }
>
> template<class T>
> std::optional<T> contained_value(std::weak_ptr<T>& item) {
> return contained_value(std::as_const(item));
> }
>
> (For weak_ptr, if you don't want a copy of the T, you can pass
> item.lock() to value_or.)
>
> --
> Eric Schmidt
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2022-11-17 17:03:50