I have a simple thread pool. It takes tasks and distributes them among threads using round-robin. The task looks like this
using TaskFn = void (*)(void*);
struct Task {
TaskFn fn;
void* args;
};
Just two pointers: to a function that takes void* and to the argument itself. The thread pool calls Task::fn and passes Task::args. Everything works well.
But I wanted to write a typed wrapper for this whole thing. So I could write like this:
Task some_task = MakeTask([](int a, int b){
// Task body
}, 123, 456);
I don’t need the closures to work. I wrote code that does not compile:
template <typename Function, typename ... Args>
void DoCall(Function f, std::tuple<Args...>* args) {
auto callable = [args, f](){
std::apply(f, *args);
};
callable();
}
template <typename Function, typename ... Args>
Task MakeTask(Function f, Args ... args) {
Task task;
std::tuple<Args...>* args_on_heap = new std::tuple<Args...>(args...);
task.args = (void*) args_on_heap;
TaskFn fn = [](void* ptr){
// The problem here is that I can’t pass `f` here without creating a closure.
// But if I create a closure, the signature will be different.
// In theory, everything that is needed is known at the compilation stage.
// But how to curb the compiler?
DoCall<Function, Args...>(f, (std::tuple<Args...>*) ptr);
};
task.fn = fn;
return task;
// P.S I know that args_on_heap leaks.
}
So, questions:
- Is it possible to implement what I have in mind?
- If yes, how to do it? In what direction should I dig? What features of the language (which I probably don’t know about yet) will help me implement what I have in mind.
- If I can’t implement this, then what are the alternatives?
Thank you in advance :)
As written, you’re accepting even callable objects with state, so the language will force you to account for that state. You could copy it into a control block along with your arguments, but if support for state isn’t the point you can require the function to be a template argument:
Note that passing a lambda directly as a template argument requires C++20. You can work around that with a constexpr variable: