Home > Software design >  How to make it so the RHS of || isn't compiled for certain situations?
How to make it so the RHS of || isn't compiled for certain situations?

Time:04-14

I have some logic that looks like this:

if (x > y || eval(x, y)) {
  // do something
}

I don't want eval(x, y) (this is a boolean function) to be compiled when some condition (known at compile time is met) is met (in this case we can just get rid of the || too, or for uniformity, we could just have eval(x, y) replaced with false).

Is there a way to achieve this?

I think one way to do this is

bool rhs = false;
if constexpr ( condition ) {
  rhs = eval(x, y);
}
if (x > y || rhs) {
  // do something
}

but then I lose the benefit of short circuiting here. I don't want to call eval(x, y) until x > y is false.

CodePudding user response:

You can wrap it with lambda

auto maybe_eval = [](auto x, auto y) {
  if constexpr (some condition) 
    return false;
  else 
    return eval(x, y)
};

if (x > y || maybe_eval(x, y)) {
  // do something
}

CodePudding user response:

Short-circuit eval of false && eval(x,y) will make that call into dead code.
康桓瑋 points out that short-circuit evaluation does not work when some_condition makes eval(x,y) ill-formed; in that case see their answer for a lambda. In this answer, it still has to be able to compile, but it compiles to no asm.

void foo(int x, int y) {
    if (x>y || (condition && eval(x,y))) {
        sink = 1;   // store to a volatile global is easy to see in compiler asm output
    }
}
  • With condition = false, it's the same as if(x>y || false), i.e. if(x>y).
  • With condition = true, it's the same as if(x>y || eval(x,y))

Real compilers do in practice do this short-circuit eval to omit any call instruction to eval. For GCC and clang at least, that dead code removal happens even with optimization disabled. See it on the Godbolt compiler explorer with GCC and clang. With condition = true, this function compiles to the same asm as the simple original using if (x > y || eval(x, y)). With condition = false it simplifies it to only check the x > y condition, as if eval always returned 0.

Fun fact: with __attribute__((const)) int eval(int, int); or pure, clang will even optimize your if constexpr attempt into the same asm, since it knows that the call doesn't have side effects so it's ok to not call it.

  • Related