A task executor is given tasks that it runs. A while ago I designed a small concept of a task executor that replaces dynamic polymorphism with static polymorphism. But while switching from dynamic to static I lost an aspect about the type of the task that is being passed to the executor: its shape.
The dynamic approach requires an interface that describes exactly how a task must look: what method is required and what’s that method’s signature. For the static approach, I had nothing but a compile-time error if the task does not have a required method. The error message is good, I’m OK with what I get. But I don’t have a definition of what my constraints are for the task. I don’t have a concept of my requirement.
Before C++20, things were verbose and somewhat complicated. There are some ways to write requirements using SFINAE. But I feel they are just for the compilation to fail if they are not met. As for a human to understand them, they sure need more than a glance. It feels like before understanding the requirements of a type, you first need to understand how those requirements are implemented.
I tried a few implementations myself, but I could not get them exactly as I would like them to be. I’m having in my mind the simplicity that C++20 has on the concepts topic and I wanted to be around it. So… why not give C++20 a try? I never wrote C++20 more than a few experimental lines, so I wanted to see how I could implement my need.
A stripped off executor just for the sake of the example, with a task to be executed, would be:
int executor(int input, auto&& task) { return task.execute(input); } struct Task { int execute(int input) { return input + 1; } }; int main() { executor(1, Task{}); }
The requirements that need to be implemented by the task are:
-
- It must be a type with a method named
execute
. - The method accepts an integer argument
- and returns an integer.
- It must be a type with a method named
And I need to define a C++20 concept that requires a type T
representing the task and an int
which will be the input: I call the required execute
method on the task
object, with the integer argument, and I verify that the return type is an integer. Continue reading Check if object has method with C++20 concepts