Cull Null Distribution Operator

Has anyone implemented a NULL distribution operator in C ++, similar to the one used in functional languages? I’m thinking of some kind of smart template, possibly related to the spread of operator behavior →.

Suppose we have a chain of objects from a graph, for example foo->bar->baz(sorry, Demeter’s law). Suppose that any of them can be null and must be tested before dereferencing. Then the code becomes much more complicated:

if( !foo )
  return nullptr;
if( !foo->bar )
  return nullptr;
return foo->bar->baz;

I would like to “filter out” null checks using some compact syntax:

foo?->bar?->baz   // imaginary null-propagation syntax

Now, of course, it should not look like this, just be almost as compact. I assume that you need a monad in C ++, which allows you to use a null test and "continue". It would be great to avoid macros and lambda, but this is probably not possible. I could imagine an operator overload → () at each step and a short circuit if thisit was zero.

But it is very intrusive. An ideal solution would wrap every object in the chain.

+5
source share
3 answers

At this point in your design, this may be a radical departure, but perhaps consider using a Null Object Pattern . Then you do not need any checks at all.

+8
source

TL;DR; , ++ .

, , ( ).

type function(type parent) {
    if (parent==nullptr || parent->child==nullptr)
        return null;
    //do stuff with parent->child->grandchild
}

" → () , null.", , ( ). , operator->() return null, this - null, operator->() undefined. , , , null. , , , .

, -,

#define MAYBENULL1(X, Y) (X?(X->Y,nullptr)
#define MAYBENULL2(X, Y, Z) (X&&X->Y?X->Y->Z,nullptr)
#define MAYBENULL3(X, Y, Z, W) (X&&X->Y&&X->Y->Z?X->Y->Z->W,nullptr)

type function(type parent) {return MAYBENULL2(parent, child, grandchild);}

, - .

parent* get_next_parent();
type function() {return MAYBENULL1(get_next_parent(), child);}
+1

# . , , if foo null, null, foo null, foo->bar.

++.

return (foo) ? (foo->bar) ? foo->bar->baz : nullptr : nullptr;

, , , .

#define Maybe(X, Y) (X)?X->Y:nullptr
// Attempted nested usage:
return Maybe(foo, Maybe(foo->bar, baz));
//     ^ VS2015 complains here saying "Expected a member name".

, , , , , .

, " ", Mooing Duck .

template<typename T_Return, typename T_X, T_Return* T_X::*Y>
T_Return* Maybe(T_X* pX)
{
    return (pX) ? pX->*Y : nullptr;
}

//Usage:
// Type is required, so assume the struct Foo contains a Bar* and Bar contains a Baz*.
return Maybe<Baz, Bar, &Bar::baz>(Maybe<Bar, Foo, &Foo::bar(foo));

, , .

, , , Null Propagation, , , "" . :

// C# code here.
if(foo != null) { foo.Bar(); }

# :

foo?.Bar()

, "" .

// C++ Null Propagation
#define Maybe(X) if (auto tempX = (X)) tempX

//Usage
Maybe(GetFoo())->Bar();

//Or for setting a variable:
Maybe(GetFoo()->bar = new Bar();

, ?: inline if.

, .

, - :

#define ReturnMaybe(X, Y) auto tempX = X; \
if (tempX != nullptr) \
{ \
    return tempX->Y; \
} \
else \
{ \
    return nullptr; \
} \

:

#define ReturnMaybe(X, Y) auto tempX = X; if(tempX) { return tempX->Y; }else{return nullptr;}

// Usage
Bar* GetBar()
{
    ReturnMaybe(GetFoo(), bar)
}

(, , ), - .

Change Made the template answer a little better.

Edit 2 - talked about this with colleagues and came up with some other options:

#define Maybe(X) (X==nullptr) ? nullptr : X

Using:

return Maybe(foo)->bar;

Interestingly, this also works in case of a call.

Maybe(foo)->Bar();

because what it expands is indeed (albeit strange)

(foo == nullptr)? nullptr: foo-> Bar ();

However, since you cannot put a variable declaration in ?: shorthand, you cannot protect against

return Maybe(get_next_parent())->child;

as described in Mooing Duck, and you will need to create a temporary place.

+1
source

All Articles