How to avoid nested if statements with chained optionals in C++?
For example, if type A contains an std::optional<B> b
and type B an std::optional<C> c
, I would like to be able to write something like:
const auto v = if_exists(if_exists(a->b)->c);
And v would get the value from c or an empty optional if either b or c are empty optionals.
I think this would be nicer that nested ifs like this:
if (a->b) {
const auto b = *(a->b);
if (b->c) {
const auto c = *(b->c);
}
}
The following question seems to go in this direction but I am not sure how to adapt it to my use-case: Haskell style "Maybe" type & *chaining* in C++11
You can do something like this (pseudocode-ish; link to buildable code is provided below):
// wrap std::optional for chaining
template <class T> class Maybe {
std::optional<T> t;
// ... constructors etc
// Maybe chaining
// If A has a member named m of type M,
// then Maybe<A>.fetch(&A::m) returns a Maybe<M>
template <class M>
Maybe<M> fetch(M T::*mem_ptr) {
return (bool(t)) ? Maybe<M>((*t).*mem_ptr) : Maybe<M>() ;
}
// Maybe chaining special case
// If A has a member named m, which is itself a Maybe<M>,
// then return it without wrapping it in an additional Maybe
template <class M>
Maybe<M> fetch(Maybe<M> T::*mem_ptr) {
return (bool(t)) ? ((*t).*mem_ptr) : Maybe<M>() ;
}
};
Now if you have this:
struct C { int d ; }
struct B { C c; }
struct A { B b; }
A a;
Maybe<A> ma;
and you can do this
int d = a.b.c.d;
you cannot do the same with ma
, but you can use the next best thing, namely:
Maybe<int> md = ma.fetch(&A::b).fetch(&B::c).fetch(&C::d);
And you can still use this if you Maybe
-ify any or all struct
members above:
struct C { Maybe<int> d ; }
struct B { Maybe<C> c; }
struct A { Maybe<B> b; }