Question

I want to be able avoid calling a function when a condition is false when this is known at compile time. Now I use something like this:

template<bool Enabled>
void fun(params)
{
    //do nothing
}

template<>
void fun<true>(params)
{
    //do something with params.
}

The thing I don't like with this aproach is that params are evaluated even if the function body is empty.

I would like a solution when the function is not called at all and the params won't be evaluated when the condition is false(this might be optimized in the first case with empty function, but I can't assume this is true on every compiler).

Is this even possible?

Was it helpful?

Solution

Yep.

template<bool b, typename Func, typename FuncElse>
struct compile_time_if_functor {
  void operator()( Func&& f, FuncElse&& ) const{
    std::forward<Func>(f)();
  }
};
template<typename Func, typename FuncElse>
struct compile_time_if_functor<false, Func, FuncElse> {
  void operator()( Func&&, FuncElse&& else_f  ) const{
    std:forward<FuncElse>(else_f)();
  }
};
template<bool b, typename Func, typename Else>
void compile_time_if( Func&& f, Else&& elsef ) {
  compile_time_if_functor<b, Func, Else> functor;
  functor(
    std::forward<Func>(f),
    std::forward<Else>(elsef)
  );
}
template<bool b, typename Func>
void compile_time_if( Func&& f ) {
  auto do_nothing = []{};
  compile_time_if<b>( std::forward<Func>(f), std::move(do_nothing) );
}

use:

int main() {
  compile_time_if<expression>([&]{
    // code that runs iff expression is true
  });
  compile_time_if<expression2>([&]{
    // code that runs iff expression2 is true
  },[&]{
    // else clause, runs iff expression2 is false
  });
}

note that the code inside the {} is compiled, but does not run, if it is in the wrong branch of the if. So that code needs to be well formed and legal at the type level, but it doesn't have to be legal to execute at run time. No creating arrays of size 0 in those lambdas!

A fancier method would let you chain if-else blocks indefinitely. I'd use named operator tricks if I wanted to do that.

First, change compile_time_if to return std::integral_constant< bool, b >.

Then, write compile_time_else(Func&&) and compile_time_elseif<bool>(Func&&) which return types that package that Func and override operator*( std::true_type, X ) and operator*( std::false_type, X ) to run or not run Func and return std::true_type or std::false_type.

The end goal would be this syntax:

If<expression>([&]{
  // block 1
})*Else([&]{
  // block 2
});

If<expression>([&]{
  // block 1
})*ElseIf<expression2>([&]{
  // block 2
})*ElseIf<expression3>([&]{
  // block 3
})*Else([&]{
  // block 4
});

allowing full-on cascading flow control. You could even go so far as to do:

compile_time_switch<value>(
  Case<c0, FallThrough>(),
  Case<c1>([&]{
    // block 1
  }),
  Case<c2, Continue>([&]{
    // block 2
  }),
  Case<c3>([&]{
    // block 3
  }),
  Case<c4>([&]->CanContinue{
    // block 4
    if (condition) {
      return Continue;
    } else {
      return Break;
    }
  }),
  Case<c4>([&]{
  }),
  Default([&]{
  })
};

but that is getting ahead of ourselves.

For ideas on how to mess around with duplicating C++ syntax in a way that allows the compiler to manipulate flow control at compile time, look at boost phoenix. I'm just including this here for completeness: actually writing that kind of stuff up is not all that practical, as some poor sod is going to have to maintain it, and the first few times you write this stuff up you are going to do a poor job!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top