Reputation: 97
I'm implementing a Strategy Design Pattern in c++, and I've found a couple of different options I could take. One route would be to use a template, and might look like this:
namespace Strategies
{
struct IsEven
{
bool doSomething(int x) { return x % 2 == 0; }
};
struct IsOdd
{
bool doSomething(int y) { return y % 2 == 1; }
};
}
template<typename Strategy>
class Processor
{
public:
Processor() : _strat{ Strategy() }
{}
bool process(int num)
{
return _strat.doSomething(num);
}
private:
Strategy _strat;
};
int main()
{
Processor<Strategies::IsEven> templateOne{};
Processor<Strategies::IsOdd> templateTwo{};
std::cout << "Process 4 with One: " << (templateOne.process(4) ? "True" : "False") << std::endl;
std::cout << "Process 4 with Two: " << (templateTwo.process(4) ? "True" : "False") << std::endl;
}
Another route would be to use function pointers, and might look like this:
#include <functional>
class ProcessorTwo
{
public:
using StrategyFunction = std::function<bool(int)>;
static bool lessThanFive(int num) {
return num < 5;
}
static bool greaterThanFive(int num) {
return num > 5;
}
ProcessorTwo(StrategyFunction strat) : _strat{ strat }
{}
bool process(int num) {
return _strat(num);
}
private:
StrategyFunction _strat;
};
int main()
{
ProcessorTwo functionPointerOne{ ProcessorTwo::greaterThanFive };
ProcessorTwo functionPointerTwo{ ProcessorTwo::lessThanFive };
std::cout << "Process 4 with One: " << (functionPointerOne.process(4) ? "True" : "False") << std::endl;
std::cout << "Process 4 with Two: " << (functionPointerTwo.process(4) ? "True" : "False") << std::endl;
}
I will know the functions that will be used at compile time. I'm new enough to c++ that I don't really understand what the differences between these two approaches would be. I've heard function pointers add a layer of indirection, but wouldn't the same also be true of a struct holding the function? Is there a more "idiomatic" c++ option, or does it just come down to developer preference?
Upvotes: 1
Views: 207
Reputation: 9555
The main difference is the template version uses "compile time polymorphism" meaning the selection of the strategy happens at compile time and the strategy is part of the type. Thus, the strategy a particular instance of Processor<T>
uses must be known at compile time and cannot be changed dynamically after a Processor<T>
is created.
You could add a member function, however, to make it so the strategy used by a ProcessorTwo
can be changed dynamically. You could also write, say, a function that takes a std::function<bool(int)>
as an argument and creates a ProcessorTwo
with it in the function's body; you couldn't do this with a Processor<T>
unless it was a function template.
So if you need that kind of dynamism a function pointer or std::function
based approach is the way to go, but if you don't need it then the template version will be more efficient because calling the strategy will not involve runtime indirection.
Upvotes: 1