Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an idiom like `if (Value * value = getValue())` when you branch on an expression of the retrieved value?

Tags:

c++

qt

qstring

I am often using the common

if (Value * value = getValue())
{
    // do something with value
}
else
{
    // handle lack of value
}

Now, I also often do

QString error = someFunctionReturningAnErrorString(arg);
if (!error.isEmpty())
{
     // handle the error
}
// empty error means: no error

That's all fine but I would like the error variable to be scoped to the if-block. Is there a nice idiom for that? Obviously, I can just wrap the whole part inside another block.

This, obviously, does not work:

if(QString error = someFunctionReturningAnErrorString(arg), !error.isEmpty())
{
    // handle the error
}
// empty error means: no error

And unfortunately (but for good reasons) the QString cannot be converted to bool, so this does not work either:

if(QString error = someFunctionReturningAnErrorString(arg))
{
    // handle the error
}
// empty error means: no error

Any suggestions?

like image 239
Tilman Vogel Avatar asked Jun 11 '15 19:06

Tilman Vogel


4 Answers

No. There is no idiom like this, and there is no syntax like this!

Besides, you have reached the point at which it is no longer worthwhile to make your code more and more obfuscated.

Simply write it as you do now.

If you really don't want the scope leakage, introduce a new scope:

{
   const QString error = someFunctionReturningAnErrorString(arg);
   if (!error.isEmpty()) {
      // handle the error
   }
}
// The above-declared `error` doesn't exist down here

I use this pattern quite a lot, though I've been fairly accused of scope-addiction, so take that as you will.

like image 134
Lightness Races in Orbit Avatar answered Oct 31 '22 18:10

Lightness Races in Orbit


The only way to use that idiom while still keeping your code understandable is if your function returns an object that is convertible to bool in a way that true indicates that you want to take the branch and false means that you do not care about it. Anything else is just going to lead to write-only code.

One such object which may be relevant happens to be boost::optional. Given:

boost::optional<QString> someFunctionReturningAnErrorString(T arg);

You could use the idiom you want in a natural way:

if (auto error = someFunctionReturningAnErrorString(arg)) {
    // ...
}

This also has the added benefit where I'd consider an optional error message more semantically meaningful than having to check for an empty error message.

like image 22
Barry Avatar answered Oct 31 '22 18:10

Barry


There is basically no clean way to do that.

I'd recommend you just define an extra block around the if, but if you really want to have that exact syntax, a solution could be to declare your own class wrapping QString:

struct ErrorString
{
    ErrorString(QString&& s) : s{move(s)} {}
    operator bool() {return !s.isEmpty();}

    QString s;
};

And then you could write:

if(ErrorString error = someFunctionReturningAnErrorString(arg))
{
    // handle the error
}
// empty error means: no error

But I'm not particularly fond of this solution.

like image 3
tux3 Avatar answered Oct 31 '22 19:10

tux3


You could use:

for(QString error = someFunctionReturningAnErrorString(arg); !error.isEmpty(); /* too bad 'break' is invalid here */)
{
    // handle the error
    break;
}

but this is ugly, and makes your code hard to read. So please don't.

like image 2
user253751 Avatar answered Oct 31 '22 18:10

user253751