@VadimP22

Как написать типизированную обёртку над простым тред-пулом?

Привет всем. У меня есть простенький тред пул. Он берет задачи и раскидывает их по потокам 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;
}
  • Вопрос задан
  • 135 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы