Привет всем. У меня есть простенький тред пул. Он берет задачи и раскидывает их по потокам round-robin'ом.
Задача выглядит так:
using TaskFn = void (*)(void*);
struct Task {
TaskFn fn;
void* args;
};
Т.е просто два указателя: на функцию, которая принимает void* и на сам аргумент. Тред пул вызывает Task::fn и передает туда task::args. Всё вроде бы нелохо работает.
Но мне захотелось написать типизированную обёртку над всем этим делом. Т.е чтобы можно было написать так:
Task some_task = MakeTask([](int a, int b){
// Task body
}, 123, 456);
Сразу скажу, мне не требуется чтобы работали замыкания. Вобщем, я написал такой код, который не компилируется.
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){
// Тут проблема в том что у меня не получается передать f сюда без создания замыкания.
// Но если создать замыкание, то сигнатура будет другая.
// По-идее всё что нужно известно на этапе компиляции. Но как обуздать компилятор?
DoCall<Function, Args...>(f, (std::tuple<Args...>*) ptr);
};
task.fn = fn;
return task;
// P.S Я знаю что тут утекает память, т.к. не освобождается args_on_heap.
}
Итак, вопросы:
1. Возможно ли реализовать то что я задумал?
2. Если да, то как это сделать? В каком направлении копать? Какие фичи языка (о которых я, вероятно, еще не знаю) помогут мне реализовать то что я задумал.
3. Если в том виде что у меня это не реализовать, то какие есть альтернативы?
Заранее спасибо :)
ОБНОВЛЕНО:
Я нашел решение! Начиная с C++20 лямбды без замыканий default-costructible, поэтому я написал такой код:
// Это без аллокаций
template<typename Function, typename Arg> Task MakeTask(Function function, Arg* arg) {
Task task;
task.args = (void*) arg;
task.fn = [](void* ptr){
Function f;
f((Arg*) ptr);
};
return task;
}
// А это с аллокацией
template<typename ... Args> Task MakeTask(auto function, Args ... args) {
Task task;
auto args_on_heap = (void*) new std::tuple<Args...>(args...);
task.args = args_on_heap;
task.fn = [](void* ptr){
decltype(function) f;
auto largs = (std::tuple<Args...>*) ptr;
std::apply(f, *largs);
delete largs;
};
return task;
}