0

To avoid an exception, one must check each pointer in a chain of pointers before making use of the inner pointer.

Is there a modern c++ syntax, or some easy way to replace the tedious:

if (pA && pA->pB && pA->pB->pC)
    pA->pB->pC->DoYerThing();

Into something like the following?

if(not_nullptrs(pA->pB->pC))
    pA->pB->pC->DoYerThing();
gil_mo
  • 575
  • 1
  • 6
  • 27
  • 5
    "To avoid an exception" - Dereferencing a `nullptr` or uninitialised pointer does *not* result in an exception. It results in Undefined Behaviour. – Jesper Juhl Oct 22 '18 at 09:59
  • I don't think I've ever needed to write code like that. What is your use case? – melpomene Oct 22 '18 at 10:06
  • 2
    TL;DR: No, there isn't. Leaking out and having to dig through multiple levels of implementation details is code smell. I suggest you rethink your design. – WhiZTiM Oct 22 '18 at 10:21
  • Not using STL types (yet) or regular pointers. But it's pretty trivial to write your own simple `optional` for pointers with bind/map instructions (a-la Rust or almost any other language with similar type). That way you could write `pA.bind([&](auto p){ return pB; }).bind(...)`, you get the idea. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0798r0.html – Dan M. Oct 22 '18 at 10:22
  • 1
    @DanM.: It is an EXACT duplicate of my question... – gil_mo Oct 22 '18 at 10:31

1 Answers1

2

Here is a fixed version of Romeo's suggestion:

template <typename X0>
auto safe_chain(X0* x0)
{
    return x0;
}

template <typename X0, typename X1, typename... Xs>
auto safe_chain(X0* x0, X1 x1, Xs... xs)
{
    return x0
        ? safe_chain(x0->*x1, xs...)
        : nullptr;
}


// usage
struct C {
    void DoYerThing();
};
struct B {
    C* pC;
};
struct A {
    B* pB;
};

if(auto p = safe_chain(pA, &A::pB, &B::pC))
    p->DoYerThing();

This isn't exactly easy to read. pA && pA->pB && pA->pB->pC is just fine.

The idiomatic solution is usually to rely on references whenever possible, so that null pointer checks are limited to as few as possible. When a pointer is really needed, you should use a function (possibly a member function of *pA) to avoid duplicating the checks.

eerorika
  • 232,697
  • 12
  • 197
  • 326