6

I'm trying to optimize out some function at compile time by using an enum class as a template parameter.

Say for example

enum class Color { RED, BLACK };

Now, I would like to define a method

void myMethod<Color c> () {
 if( c == Color::RED ) { ... }
 if( c == Color::BLACK ) { ... }
}

And I would like the compiler to make 2 copies of myMethod and eliminate the dead code during the optimisation (it's for CUDA kernels so speed and register usage is important to me)

However, seems like when I call the method using

void doSomething( const Color c ) {
 myMethod<c>();
}

MSVC complains with "expression must have a constant value". I was expecting the compiler to be clever enough to compile a version of myMethod with each possible version of the enum. Is that not the case ? Can I force it to, without an ugly switch in doSomething ?

Thanks for your help !

5
  • Somewhere you have to decide in run-time which myMethod<Color>() you want to call, if it's dependent of a non-constant variable. So, you are back to a switch() (or an if-else-cascade, or an array mapping colors to function pointers, or something else). Commented Feb 26, 2020 at 8:54
  • 1
    This seems to happen often recently. const doesn't make a (compile time) constant expresion, i.e. a expression that can be evaluated at compile time. Commented Feb 26, 2020 at 8:54
  • 1
    in a nutshell: template = compile time, ordinary function parameters = runtime. You can mix, but then you need to define how to mix, eg as already mentioned with a switch to choose which function to call Commented Feb 26, 2020 at 8:56
  • Fair enough, but irrelevant of the const expression, I still thought the compiler would somehow read out my enum and decide at runtime based on the value of the argument. Ok maybe I expect too much... Thank you for the help :) Commented Feb 26, 2020 at 8:59
  • What you might want to achieve, I once applied to a fun project to move ifs out of the most inner loops to the outside. Of course, the nested loops had to be re-implemented for every case. There I used the template-with-value-parameters to prevent code duplication. the template, the call of that template and a little explanation Optimization Attempts. Commented Feb 26, 2020 at 9:06

1 Answer 1

10

You have to decide between run-time or compile-time evaluation. The compile-time version can be something like this:

enum class Color { RED, BLACK };

template <Color c>
void myMethod () {
    if constexpr ( c == Color::RED ) { std::cout << "RED" << std::endl; }
    if constexpr ( c == Color::BLACK ) { std::cout << "BLACK" << std::endl; }
}   

int main()
{   
    myMethod<Color::RED>();
    myMethod<Color::BLACK>();
}

But, if you need to evaluate the variable at run-time, you have to switch over all of the possible values:

enum class Color { RED, BLACK };

template <Color c>  
void myMethod () {
    if constexpr ( c == Color::RED ) { std::cout << "RED" << std::endl; }
    if constexpr ( c == Color::BLACK ) { std::cout << "BLACK" << std::endl; }
}

void RuntimeDispatch( Color c ) 
{
    if ( c == Color::RED ) { myMethod<Color::RED>(); }
    if ( c == Color::BLACK ) { myMethod<Color::BLACK>(); }
}   

int main()
{   
    RuntimeDispatch( Color::RED );
    RuntimeDispatch( Color::BLACK );
}

There is simply no way to use a run-time variable as a template parameter, because the value is not a known constant at compile-time.

If you have to use an older compiler, you can replace constexpr if with template specialization:

template <Color c> void myMethod ();

template <> void myMethod<Color::RED>() { std::cout << "RED" << std::endl; }
template <> void myMethod<Color::BLACK>() { std::cout << "BLACK" << std::endl; }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.