Implementing type-safe command patterns using variadic templates in C++

In object-oriented programming, the Command pattern is a behavioral design pattern that encapsulates a request as an object, thereby allowing developers to parameterize clients with queues, requests, and operations. It promotes loose coupling, separation of concerns, and the ability to undo and redo commands.

One challenge when implementing the Command pattern is ensuring type safety. In C++, one way to achieve this is by using variadic templates. Variadic templates allow functions and classes to accept an arbitrary number of arguments of different types.

Creating the Command Interface

First, let’s define the command interface, which all commands will implement. This interface will typically include an execute() method that will be called to execute the command.

class Command {
public:
    virtual void execute() = 0;
    virtual ~Command() {};
};

Creating Concrete Command Classes

Next, we’ll create concrete command classes that inherit from the Command interface. Each of these classes will implement the execute() method according to the specific behavior it should exhibit when executed.

class PrintCommand : public Command {
public:
    void execute() override {
        std::cout << "Executing PrintCommand" << std::endl;
    }
};

class SaveCommand : public Command {
public:
    void execute() override {
        std::cout << "Executing SaveCommand" << std::endl;
    }
};

class DeleteCommand : public Command {
public:
    void execute() override {
        std::cout << "Executing DeleteCommand" << std::endl;
    }
};

Creating the Invoker

The invoker class is responsible for executing the commands. It should provide a way to store and execute commands. Here, we’ll use a std::deque to store the commands.

class Invoker {
private:
    std::deque<std::unique_ptr<Command>> commandQueue;

public:
    void addCommand(std::unique_ptr<Command> command) {
        commandQueue.push_back(std::move(command));
    }

    void executeCommands() {
        while (!commandQueue.empty()) {
            std::unique_ptr<Command> command = std::move(commandQueue.front());
            commandQueue.pop_front();
            command->execute();
        }
    }
};

Putting It All Together

Using the command pattern, we can now create instances of the concrete command classes, add them to the invoker’s queue, and execute them.

int main() {
    Invoker invoker;

    std::unique_ptr<Command> printCommand = std::make_unique<PrintCommand>();
    std::unique_ptr<Command> saveCommand = std::make_unique<SaveCommand>();
    std::unique_ptr<Command> deleteCommand = std::make_unique<DeleteCommand>();

    invoker.addCommand(std::move(printCommand));
    invoker.addCommand(std::move(saveCommand));
    invoker.addCommand(std::move(deleteCommand));

    invoker.executeCommands();

    return 0;
}

Conclusion

Using variadic templates in C++, we can implement type-safe command patterns. This allows us to encapsulate and execute commands in a flexible and extensible manner. By leveraging the power of variadic templates, we can ensure type safety and achieve more robust and maintainable code.

#C++ #DesignPatterns