Reputation: 1015
I would like to pass different classes as an argument to a function, but I am not sure how to do it in c++. The classes I want to pass have member functions with the same name. I wrote my program in JavaScript
:
class Calculator {
constructor(value) {
this.value = value;
}
// here I want to pass in AddCommand and SubtractCommand class types
executeCommand(command) {
this.value = command.execute(this.value); // They both have .execute methods
}
}
class AddCommand {
constructor(valueToAdd) {
this.valueToAdd = valueToAdd;
}
execute(currValue) {
return currValue + this.valueToAdd;
}
undo(currValue) {
return currValue - this.valueToAdd;
}
}
class SubtractCommand {
constructor(valueToSubtract) {
this.valueToSubtract = valueToSubtract;
}
execute(currValue) {
return currValue - this.valueToSubtract;
}
undo(currValue) {
return currValue + this.valueToSubtract;
}
}
As you can see my AddCommand
and SubtractCommand
classes both have execute
member functions, and I want to pass instances of them to the executeCommand
method in the Calculator
class. How should I do it? I know the example might be trivial, but I am just asking how to deal with a case like this in general.
I can perform add and subtract like this:
let calculator = new Calculator(0);
calculator.executeCommand(new AddCommand(5));
console.log(calculator.value); // 5
calculator.executeCommand(new SubtractCommand(5));
console.log(calculator.value); // 0
Thank you for your help!
Upvotes: 0
Views: 899
Reputation:
There's a few different to ways to tackle this problem depending on the specific needs of your program.
Here are the two techniques that are most commonly used:
This is appropriate, and generally preferable, when the type of the command is known at the call site. It involves making executeCommand()
a function template, i.e a function that produces multiple implementations "on-demand" based on the arguments passed to it.
In modern (c++20) C++, you can use a concept to make the expected interface explicit. In older versions of the language, you would make the executeCommand()
method accept any type and just use it as if the required methods are implemented. This is called duck-typing.
#include <concepts>
template<typename T>
concept Command = requires(const T& cmd, int v) {
{ cmd.execute(v) } -> std::convertible_to<int>;
{ cmd.undo(v) } -> std::convertible_to<int>;
};
class Calculator {
int value;
public:
Calculator(int v) : value(v) {}
template<Command CmdT>
void executeCommand(const CmdT& command) {
value = command.execute(value);
}
// For C++17 and earlier:
// template<typename CmdT>
// void executeCommand(const CmdT& command) {
// value = command.execute(value);
// }
};
class AddCommand {
int valueToAdd;
public:
AddCommand(int v) : valueToAdd(v){}
int execute(int currValue) const {
return currValue + valueToAdd;
}
int undo(int currValue) const {
return currValue - valueToAdd;
}
};
int main() {
Calculator calc{0};
AddCommand command{3};
calc.executeCommand(command);
}
This is appropriate if the actual type of the command is erased by the time it's used. In this scenario, you declare a base interface class that the various implementations inherit from. Invoking the methods on pointers and references to the base class will call the subclass's implementations.
class Command {
public:
virtual ~Command() {}
virtual int execute(int currValue) const = 0;
virtual int undo(int currValue) const = 0;
};
class Calculator {
int value;
public:
Calculator(int v) : value(v) {}
void executeCommand(const Command& command) {
value = command.execute(value);
}
};
class AddCommand final : public Command {
int valueToAdd;
public:
AddCommand(int v) : valueToAdd(v) {}
int execute(int currValue) const override {
return currValue + valueToAdd;
}
int undo(int currValue) const override {
return currValue - valueToAdd;
}
};
void process(const Command& cmd) {
Calculator calc{0};
calc.executeCommand(cmd);
}
int main() {
AddCommand command{3};
process(command);
}
Upvotes: 3