13

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?

Carlton
  • 4,217
  • 2
  • 24
  • 40
Tilman Vogel
  • 9,337
  • 4
  • 33
  • 32
  • 1
    I've changed my coding guidelines so that assignment statements are never in a conditional expression. This is to avoid entering the "==" vs. "=" issue in `if` statements. I let the compiler take care of temporary variables. – Thomas Matthews Jun 11 '15 at 19:47
  • Do you need the error variable to use in your error handling? You could use `if(!someFunctionReturningAnErrorString(arg).isEmpty())` instead. – Olivier Poulin Jun 11 '15 at 19:48
  • 3
    Do not shorten your code needlessly, just have some intermediate variables making reading and debugging the code easier. (In other words: Your coding style is obfuscating) –  Jun 11 '15 at 19:49
  • 1
    FWIW, `QString error; if (error = getError(), !error.isEmpty()) { … }` is possible. – Emil Laine Jun 11 '15 at 19:53
  • 3
    @JonathanPotter If you're referring to the variable declaration inside the `if()` statement, it's perfectly valid, and I see it quite often. It is particularly useful when dealing with `weak_ptr`. – Apples Jun 11 '15 at 19:54
  • @JonathanPotter: You could _try_ it before posting :P – Lightness Races in Orbit Jun 11 '15 at 19:57
  • @LightnessRacesinOrbit Actually I did: http://ideone.com/hAFI7a - I might be having a brain fart but I didn't think this worked. – Jonathan Potter Jun 11 '15 at 20:02
  • 1
    @JonathanPotter: It would help if you compiled it as the language we're actually talking about. As far as "proving" examples go, that one's pretty useless. [Here's what you _should_ have run!](http://ideone.com/gC2lse) – Lightness Races in Orbit Jun 11 '15 at 20:02
  • @LightnessRacesinOrbit god damn ideone defaulting to C :( ok ignore me – Jonathan Potter Jun 11 '15 at 20:03
  • @JonathanPotter: FWIW, it defaults to whatever you used last ;) – Lightness Races in Orbit Jun 11 '15 at 20:03
  • You might write shorter functions, which do not lead to your concern –  Jun 11 '15 at 20:11
  • @ThomasMatthews For some strange reason this often quoted `=`/`==` problem basically never has bitten me (in many years), apart from gcc warnings complaining. I think, it is because I usually only use assignment inside `if` when initializing a block-local variable. – Tilman Vogel Jun 12 '15 at 07:51
  • @OlivierPoulin Right, I need the non-empty value in the block, otherwise my question wouldn't have arisen. – Tilman Vogel Jun 12 '15 at 07:52
  • @zenith This does not confine `error` to the `if`-block. – Tilman Vogel Jun 12 '15 at 07:53

6 Answers6

10

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.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
6

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 3
    "I'd consider an `optional` error message more semantically meaningful than having to check for an empty error message." +1 – Emil Laine Jun 11 '15 at 20:03
  • 1
    I accepted this answer because I think it's the best suggestion for a redesign of my use case and even though it does not provide a general solution to the case of arbitrary expressions where the answers of Yakk and jxh are helpful. – Tilman Vogel Jun 12 '15 at 08:04
3

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.

tux3
  • 7,171
  • 6
  • 39
  • 51
2

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.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • This is exactly what I was looking for ;-) Only draw-back (apart from ugliness) is that I couldn't easily add an `else` clause... – Tilman Vogel Jun 12 '15 at 07:38
1
if(auto message = maybe_filter( getError(arg), [](auto&&str){
  return !str.isEmpty();
}) {
}

where maybe_filter takes a T and a test function and returns optional<T>. The optional<T> is empty if evalutating the test function on the T gives you false, and T otherwise.

Or really, modify your error getting API to return an optional string.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Ok, so if I typically only check for `isEmpty()`, I could also use an `ignore_empty()` function which has the test function hard-coded. Which would make this quite convenient to use. – Tilman Vogel Jun 12 '15 at 07:45
0

You can use a lambda.

auto error_string_handler = [](QString && error) {
    if (error.isEmpty()) return;
    //...
}

error_string_handler(someFunctionReturningAnErrorString(arg));
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Ok, but I'd need a separate lambda for each type of error handling and the error handling is visually too separated from the error producing call for me (and in reverse order). Thanks for the suggestion, though! – Tilman Vogel Jun 12 '15 at 07:39