Search code examples
c++templatesperfect-forwarding

How to pass template parameter of a function to a lambda in the function


  template <typename C, typename F, typename... Args>
  void MyClass::run(F C::*f, C* c, Args&&... args) {
    td_ = std::make_unique<std::thread>(
        [](F C::*f, C* c, Args... args){    // Is this correct?
            c.f(args...);    // should I code c.f(std::forward<Args>(args)...); here?
        });
  }

I know what std::forward is used for. But in the case above, the types of parameters are passed to a lambda, which is in the template function. I'm kind of confused now...


Solution

  • // The perfect forwarding is a bit of a thing, and you need std::apply
    // Also when writing a library like this you should give client code a chance 
    // to syncrhonize with the threads you spawn
    // AND you have to think about exception handling on the threads you spawn
    // never trust your clients not to add a function that will not throw.
    // In this example I rely on std::async/std::future to do that for now.
    
    
    #include <future>
    #include <type_traits>
    #include <iostream>
    
    //-----------------------------------------------------------------------------
    
    template<typename fn_t>
    auto run(fn_t fn) 
    {
        return std::async(std::launch::async, fn);
    }
    
    template <typename class_t, typename fn_t, typename... args_t>
    auto run(class_t& c, fn_t class_t::*fn, args_t&&... args)
    {
        // make a local lambda first so we can reuse impl of run
        // perfect forwarding of args
        // mutable is necessary to be able to call non-const methods on class_t
        auto lambda = [fn=fn, args = std::make_tuple(c, std::forward<args_t>(args) ...)]() mutable
        {
            return std::apply(fn, args);
        };
    
        return run(lambda);
    }
    
    //-----------------------------------------------------------------------------
    
    struct test_t
    {
        void fn(int value) 
        {
            std::cout << "test_t::fn(" << value << ")\n";
        };
    
        void fn_noexcept(int value) noexcept 
        {
            std::cout << "test_t::fn_noexcept(" << value << ")\n";
        };
    };
    
    //-----------------------------------------------------------------------------
    
    int main()
    {
        test_t test;
    
        auto future1 = run([&] { test.fn(1); });
        future1.get(); // give output a chance to appear
    
        auto future2 = run(test, &test_t::fn,2);
        future2.get(); // give output a chance to appear
    
        return 0;
    }