Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way for "if(T t = ...) { } else return t;"?

Is there a better way for this "idiom"?

if(State s = loadSomething()) { } else return s;

In other words, I want to do something, which may return an error (with a message) or a success state, and if there was an error I want to return it. This can become very repetitive, so I want to shorten it. For example

if(State s = loadFoobar(&loadPointer, &results)) { } else return s;
if(State s = loadBaz(&loadPointer, &results)) { } else return s;
if(State s = loadBuz(&loadPointer, &results)) { } else return s;

This must not use exceptions which I would favor otherwise (unsuitable for this build). I could write up a little class BooleanNegator<State> that stores the value, and negates its boolean evaluation. But I want to avoid doing this ad-hoc, and prefer a boost/standard solution.

like image 347
Johannes Schaub - litb Avatar asked Aug 05 '15 12:08

Johannes Schaub - litb


3 Answers

You could do:

for (State s = loadSomething(); !s; ) return s;

but I am not sure if it is more elegant, and it is definitely less readable...

like image 107
Maksim Solovjov Avatar answered Nov 19 '22 19:11

Maksim Solovjov


I assume the context is something like

State SomeFunction()
{
     if(State s = loadSomething()) { } else return s;
     return do_something_else();
}

without throwing exceptions where do_something_else() does something of relevance to SomeFunction() and returns a State. Either way, the result of continuing within the function needs to result in a State being returned, as falling off the end will cause the caller to exhibit undefined behaviour.

In that case, I would simply restructure the function to

State SomeFunction()
{
     if (State s = loadSomething())
        return do_something_else();
     else
        return s;
}

Implicit assumptions are that State has some operator (e.g. operator bool()) that can be tested, that copying a State is possible (implied by the existence of a loadSomething() that returns one) and relatively inexpensive, and that two instances of State can exist at one time.

like image 44
Peter Avatar answered Nov 19 '22 21:11

Peter


Aside from some smart/hacky uses of different keywords to get the same behavior, or adding more-or-less complex extra templates or macros to get unless() keyword or to somehow manage to inject ! operator, I'd stick to just the basic things.

This is one of the places I'd (probably) inject extra "unnecessary" brackets:

void someFunction()
{
    // some other code

    { State s = loadSomething(); if(!s) return s; }

    // some other code
}

However, in this exact case, I'd expand it to emphasise the return keyword, which can be easily overlooked when it's squashed to a one-liner. So, unless the one-liner is repeated many times and unless it's clear&obvious that there's a return inside, I'd probably write:

void someFunction()
{
    // some other code

    {
        State s = loadSomething();
        if(!s)
            return s;
    }

    // some other code
}

It might look like elevating the scope of the s, but actually it is equivalent to declaring State s in if(). All thanks to the extra brackets which explicitly limit the visibility of local s.

However, some people just "hate" seeing { .. } not coupled with a keyword/class/function/etc, or even consider it to be unreadable due to "suggesting that a if/while/etc keyword was accidentally deleted".

One more idea came to me after you added the repetitive example. You could have tried a trick known from scripting languages where && and || may return a non-bool values:

State s = loadFoobar(&loadPointer, &results);
s = s || loadBaz(&loadPointer, &results);
s = s || loadBuz(&loadPointer, &results);
if(!s) return s;

however there's a problem: in contrast to script languages, in C++ such overloads of && and || lose their short-circuit semantics which makes this attempt pointless.

However, as dyp pointed out the obvious thing, once the s scope is elevated, now simple if can be introduced back. Its visibility can be limited back again with extra {}:

{
    State s;
    if(!(s = loadFoobar(&loadPointer, &results))) return s;
    if(!(s = loadBaz(&loadPointer, &results))) return s;
    if(!(s = loadBuz(&loadPointer, &results))) return s;
}
like image 3
quetzalcoatl Avatar answered Nov 19 '22 19:11

quetzalcoatl