If you want to skip reading and get to the code, then see some kind of C++ static polymorphism in the GitHub repository.
I’m a fan of tasks. I enjoy each time I implement any kind of system that executes tasks, from very simple ones (based on a list of tasks executed on a single thread) to multithreaded ones with features like cancellation and dynamic concurrency level.
My first encounter with the idea of having a list of objects that I iterate and call a function for each one of them was when I had to implement a feature that dynamically restricted users’ access to some content based on various conditions. You can think about a lot of if
statements. I don’t like if
statements, I struggle to avoid them because each one of them brings new responsibility, test cases, and maintenance costs. And I really enjoy seeing product and management people happy when their requirements are implemented at reasonable times. And being productive is a lot about code.
A way to decouple code is, of course, to separate concerns. And if you’re in a case with multiple concerns serving the same concept, you could use a polymorphic approach. You can have an interface for a task, some tasks implementations, and something to run all the tasks when needed. Everything you’ll see in this article is a light version because I’m focusing on the idea, not on details.
#include <array> #include <iostream> #include <memory> class Task { public: virtual void Execute() = 0; }; template <typename T> struct Executor { T& tasks; void Execute() { for (auto& task : tasks) { task->Execute(); } } }; class A : public Task { public: void Execute() override { std::cout << 1; } }; class B : public Task { public: void Execute() override { std::cout << 2; } }; int main() { using Tasks = std::array<std::unique_ptr<Task>, 2>; Tasks tasks{ std::make_unique<A>(), std::make_unique<B>(), }; Executor<Tasks> executor{tasks}; executor.Execute(); }
Now I’ll add some constraints for learning purposes. Sometimes performance matters and you can’t always use the latest C++ standards, so I’ll go with these:
-
- C++ 11
- No heap allocations
- Minimum stack allocations
- No virtual methods
And I’m entering the world of templates because this can help me achieve compile-time polymorphism (C++ static polymorphism), and that’s what I’m aiming for. Continue reading Executing tasks based on static polymorphism