Reputation: 81
I have this code
class Foo {
private:
enum class Heuristic {
ONE,
TWO,
THREE
};
Heuristic h;
void select();
};
void Foo::select() {
if (h == Heuristic::ONE)
selectONE();
else if (h == Heuristic::TWO)
selectTWO();
else
selectTHREE();
}
void selectONE() {};
void selectTWO() {};
void selectTHREE() {};
Based on the value of heuristic
I want to call a specific function in select()
. I don't know the value of heuristic
at compile time, as it depends on user input. To avoid the conditional check in select()
I would like to use templates. How can I accomplish this?
Upvotes: 1
Views: 1660
Reputation: 118097
To avoid the conditional check in
select()
[...]
A simple way to avoid all conditional checks (hidden or otherwise) in select()
could be to create an array of pointers to your functions. You then look the function up by using its current Heuristic
value (which must start at 0 and not have any gaps). If the Heuristic
value changes rarely, you can even move the lookup out of select()
completely.
Example:
##include <iostream>
void selectONE() { std::cout << "one\n"; };
void selectTWO() { std::cout << "two\n"; };
void selectTHREE() { std::cout << "three\n"; };
using func_ptr_t = void(*)(); // the signature of your functions
class Foo {
public:
enum class Heuristic {
ONE,
TWO,
THREE
};
void set_heuristic(Heuristic); // a function to do the lookup
void select();
private:
Heuristic h;
func_ptr_t current_func; // a pointer to the selected function
};
void Foo::set_heuristic(Heuristic value) {
// a simple map from Heuristic value to function pointer
static const func_ptr_t funcmap[] = {
&selectONE,
&selectTWO,
&selectTHREE,
};
h = value; // perhaps not needed?
// look up the function pointer based on "h"
current_func = funcmap[static_cast<unsigned>(h)];
}
void Foo::select() {
// a pretty fast callsite:
current_func();
}
int main() {
Foo bar;
bar.set_heuristic(Foo::Heuristic::ONE);
bar.select(); // prints "one"
}
Upvotes: 2
Reputation: 40882
As it depends on runtime values there is no way to get rid of some sort of runtime checks. Which are either done by you with if
, switch
, … or by a container like std::map
, std::unordered_map
Due to that, your concern there should be readability and maintainability.
I would - like already suggested in a comment - use switch
instead of if
, but not because the compiler can optimize it better (IMHO the compiler will be able to generate the same code for both), but to allow the static analyzer to warn you about not used enums.
If the question is about performance concerns, then this should only be a problem if you call these functions at a high frequency. So if this is the case you could create a template-based entry point to your task, to which you pass the function as template argument based on the user selection:
template<auto SelectedHeuristic>
void Foo::task() {
for( /* … */ ) {
SelectedHeuristic();
}
}
void Foo::select() {
switch(h) {
case Heuristic::ONE:
Foo::task<selectONE>();
break;
case Heuristic::TWO:
Foo::task<selectTWO>();
break;
case Heuristic::THREE:
Foo::task<selectTHREE>();
break;
}
}
void selectONE() {};
void selectTWO() {};
void selectTHREE() {};
Upvotes: 4
Reputation: 48307
define a map<Heuristic, lambdas>
where the lambdas are defined as
void and taking no parameters
void f()
then take the user input and get the value of that input key and trigger the lambda
Upvotes: 0