3

If I put a T into an std::any, I can get it with any_cast<T>(my_any). But does the standard (= C++17, in the ballot phase at the moment) include a function like any_cast<T>(optional<any> oa) which returns nullopt if oa is nullopt and std::any_cast<T>(oa.value()) otherwise? Or something along those lines?

Edit: Since people seem to be suggesting implementations, I'll also list what I use for now:

/* using magic here to select between boost/std::experimental/std versions */

template<typename T>
inline const optional<T> any_cast(const optional<any>& operand)
{
    return operand ? 
        optional<T>(any_cast<T>(operand.value())) :
        optional<T>(nullopt);
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684

2 Answers2

1

There is no mention of anything like that in the std::optional proposal or in the std::any proposal.

I suppose it would be trivial to implement using a continuation function, since the return type is different depending on the state of the optional object:

template <typename T, typename TOptional, typename TF>
void any_cast_or_nullopt(TOptional&& o, TF&& f)
{
    if(!o) return; 
    f(std::any_cast<T>(*o));
}

Add static_assert and/or SFINAE where appropriate to constrain the function. The value *o should also be forwarded depending on o's value category. Example usage:

int out = -1;

std::optional<std::any> x;
x = 10;

any_cast_or_nullopt<int>(x, [&out](int value)
    {
        out = value;
    });

assert(out == 10);
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • I like your suggestion, but do you really need 3 template arguments? – einpoklum Oct 06 '16 at 15:13
  • If you want correctness and flexibility, yes: `T` is used for the `any_cast`; `TOptional` is used to perfect-forward the optional's value; `TF` is used for a generic callable object. You could implement it without `TOptional`, but you will require code repetition in order to correctly move `*o` when `o` is passed as an r-value. – Vittorio Romeo Oct 06 '16 at 15:16
  • But I don't have an f. You're forcing me to combine the casting with the use of its result - which is not like any cast. – einpoklum Oct 06 '16 at 15:25
  • @einpoklum: you asked for a function that given an `optional` and a template parameter `T` returns either a `T` instance or `nullopt` - this is impossible because `T` and `nullopt_t` are unrelated types and because the state of the `optional` is only known at run-time. You can either have your function return an `optional` *(as Barry suggested)*, or use a continuation function in order to directly use the `T` as I suggested. – Vittorio Romeo Oct 06 '16 at 15:28
  • Of course the function should returns an optional, that's what I meant by returning either a T or a nullopt... but ok, I see why you've done what you did. You've essentially implemented an `and_then`. – einpoklum Oct 06 '16 at 15:35
0

If std::optional had a bind (or and_then) member function (that is, a function on optional<T> which takes a T -> optional<U> and either invokes it or returns nullopt), then that's what you'd be looking for:

std::optional<std::any>> oa;

optional<T> opt_t = oa.bind([](std::any& v) -> std::optional<T> {
    if (T* t = std::any_cast<T>(&v)) {
        return *t;
    }
    else {
        return std::nullopt;
    }
});

or, if you really want to directly invoke any_cast<T> and deal with throwing, map:

optional<T> opt_t = oa.map([](std::any& v) {
    return std::any_cast<T>(v);
});

std::optional has no continuation functions though, so you'd have to write those as non-member functions.

Barry
  • 286,269
  • 29
  • 621
  • 977