代码之家  ›  专栏  ›  技术社区  ›  ByteMe95

如何正确扩展std::thread来包装派生线程

  •  1
  • ByteMe95  · 技术社区  · 5 年前

    使用此应用程序线程包装器的唯一目的是确保在每个派生线程上一致地调用一些代码。我原以为这很简单,但是std::thread构造函数和参数传递相当复杂,我在这里遇到了非常隐蔽的构建错误。

    这是我要做的一个简单的例子:

    #include <thread>
    
    class AppThread : public std::thread
    {
        template< class Function, class... Args > 
        static void wrap( Function&& f, Args&&... args )
        {
            //Some code
            f( std::forward<Args>( args )... );
        }
    
    public:
        template< class Function, class... Args > 
        explicit AppThread( Function&& f, Args&&... args ) : std::thread( AppThread::wrap<Function,Args...>,
                                                                          std::forward<Function>( f ), std::forward<Args>( args )... )
        {}
    };
    
    void runA() {}
    void runB( int x ) {}
    
    main()
    {
        AppThread thread1 = AppThread( runA );
        //AppThread thread2 = AppThread( runB, 5 );
    }
    

    我希望能在任何地方访问AppThread标准:螺纹因此,扩展和重写构造函数似乎是最好的方法。但是将这些参数传递给我的包装方法会导致一连串的错误(gcc7.2)

       In file included from thread_wrap.cpp:1:0:
    gcc-7.2.0/include/c++/7.2.0/thread: In instantiation of ‘struct std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >’:
    gcc-7.2.0/include/c++/7.2.0/thread:127:22:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void (&)()); _Args = {void (&)()}]’
    thread_wrap.cpp:15:130:   required from ‘AppThread::AppThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]’
    thread_wrap.cpp:24:41:   required from here
    gcc-7.2.0/include/c++/7.2.0/thread:240:2: error: no matching function for call to ‘std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_M_invoke(std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_Indices)’
      operator()()
      ^~~~~~~~
    gcc-7.2.0/include/c++/7.2.0/thread:231:4: note: candidate: template<long unsigned int ..._Ind> decltype (std::__invoke((_S_declval<_Ind>)()...)) std::thread::_Invoker<_Tuple>::_M_invoke(std::_Index_tuple<_Ind ...>) [with long unsigned int ..._Ind = {_Ind ...}; _Tuple = std::tuple<void (*)(void (&)()), void (*)()>]
        _M_invoke(_Index_tuple<_Ind...>)
        ^~~~~~~~~
    gcc-7.2.0/include/c++/7.2.0/thread:231:4: note:   template argument deduction/substitution failed:
    gcc-7.2.0/include/c++/7.2.0/thread: In substitution of ‘template<long unsigned int ..._Ind> decltype (std::__invoke(_S_declval<_Ind>()...)) std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_M_invoke<_Ind ...>(std::_Index_tuple<_Ind1 ...>) [with long unsigned int ..._Ind = {0, 1}]’:
    gcc-7.2.0/include/c++/7.2.0/thread:240:2:   required from ‘struct std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >’
    gcc-7.2.0/include/c++/7.2.0/thread:127:22:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void (&)()); _Args = {void (&)()}]’
    thread_wrap.cpp:15:130:   required from ‘AppThread::AppThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]’
    thread_wrap.cpp:24:41:   required from here
    gcc-7.2.0/include/c++/7.2.0/thread:233:29: error: no matching function for call to ‘__invoke(std::__tuple_element_t<0, std::tuple<void (*)(void (&)()), void (*)()> >, std::__tuple_element_t<1, std::tuple<void (*)(void (&)()), void (*)()> >)’
        -> decltype(std::__invoke(_S_declval<_Ind>()...))
                    ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
    In file included from gcc-7.2.0/include/c++/7.2.0/tuple:41:0,
                     from gcc-7.2.0/include/c++/7.2.0/bits/unique_ptr.h:37,
                     from gcc-7.2.0/include/c++/7.2.0/memory:80,
                     from gcc-7.2.0/include/c++/7.2.0/thread:39,
                     from thread_wrap.cpp:1:
    gcc-7.2.0/include/c++/7.2.0/bits/invoke.h:89:5: note: candidate: template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)
         __invoke(_Callable&& __fn, _Args&&... __args)
         ^~~~~~~~
    gcc-7.2.0/include/c++/7.2.0/bits/invoke.h:89:5: note:   template argument deduction/substitution failed:
    gcc-7.2.0/include/c++/7.2.0/bits/invoke.h: In substitution of ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = void (*)(void (&)()); _Args = {void (*)()}]’:
    gcc-7.2.0/include/c++/7.2.0/thread:233:29:   required by substitution of ‘template<long unsigned int ..._Ind> decltype (std::__invoke(_S_declval<_Ind>()...)) std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_M_invoke<_Ind ...>(std::_Index_tuple<_Ind1 ...>) [with long unsigned int ..._Ind = {0, 1}]’
    gcc-7.2.0/include/c++/7.2.0/thread:240:2:   required from ‘struct std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >’
    gcc-7.2.0/include/c++/7.2.0/thread:127:22:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void (&)()); _Args = {void (&)()}]’
    thread_wrap.cpp:15:130:   required from ‘AppThread::AppThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]’
    thread_wrap.cpp:24:41:   required from here
    gcc-7.2.0/include/c++/7.2.0/bits/invoke.h:89:5: error: no type named ‘type’ in ‘struct std::__invoke_result<void (*)(void (&)()), void (*)()>’
    

    0 回复  |  直到 5 年前
        1
  •  0
  •   catnip    5 年前

    #include <iostream>
    #include <thread>
    #include <functional>
    
    void preamble (void) { std::cout << "preamble\n"; }
    
    template <class F, class ... Args> std::thread ThreadWrapper (F f, Args&& ... args)
    {
        return std::thread ([f, args...] () { preamble (); f (std::forward <Args...> (args...)); });
    };
    
    int main()
    {
        std::thread t = ThreadWrapper ([] (std::string s) { std::cout << s << "\n"; }, "42");
        t.join ();
    }
    

    输出:

    preamble
    42
    

    Live demo

        2
  •  0
  •   ByteMe95    5 年前

    当我今早醒来的时候我发现了这一点:) 由于std::thread会衰减它传递给函数的所有参数,所以当它调用我的wrap方法时,这些参数就是衰减的类型。但是,当我将模板参数传递到wrap<>时,它会得到未定义的类型:

    std::thread( AppThread::wrap<Function,Args...>,  
    

    解决方案很简单,我需要在实例化模板包装方法之前衰减类型:

    std::thread( AppThread::wrap<std::decay_t<Function>,std::decay_t<Args>...>,