【问题标题】:Subclassing std::thread and forwarding packed/variadic template parameters子类化 std::thread 并转发打包/可变模板参数
【发布时间】:2016-06-25 00:35:50
【问题描述】:

在我的代码中,我希望所有线程都从一个类型继承,而该类型又从 std::thread 继承。原因很常见,我敢肯定:

  1. 来自线程外部的停止请求和来自线程内部的继续检查,无论它是哪种类型的线程,都以相同的方式完成。换句话说,每种类型的线程都没有自己的停止/检查逻辑。
  2. 在线程中运行的代码不必关心它是否真的在线程中运行,除了调用thread::shouldBeRunning(),如果不在线程中,它总是返回true。

但是,(我认为)我如何将模板参数/值从std::thread 的子类传递给包装函数存在问题,在下面的代码中是static void run(thread * This, Function&& Func, Args&&... a)

    #include <thread>
    #include <atomic>
    #include <iostream>

    class thread
    {
        friend int main(int, char **);

    private:
        std::atomic<bool> m_ShouldBeRunning;
        std::atomic<bool> m_Running;
        int               m_Error;
        std::thread       m_Thread;

    #ifdef WIN32
        __declspec(thread) static thread * m_Local;
    #else
        __thread static thread * m_Local;
    #endif

        template <typename Function, typename... Args>
        static void run(thread * This, Function&& Func, Args&&... a)
        {
            thread::m_Local = This;

            try
            {
                std::bind(std::forward<Function>(Func), std::forward<Args>(a)...)();
            }
            catch (int Error)
            {
                This->m_Error = Error;
            }
            This->m_Running.store(false, std::memory_order_release);
        }

    public:

        template <typename Function, typename... Args>
        thread(Function&& Func, Args&&... a)
            : m_ShouldBeRunning(true)
            , m_Running(true)
            , m_Error(0)
            , m_Thread(&thread::run<Function, Args...>,
                        this,
                        std::forward<Function>(Func),
                        std::forward<Args>(a)...)
        {}
        virtual ~thread()
        {
            try
            {
                stop();
                m_Thread.join();
            }
            catch (...)
            {
                std::cerr << "caught exception in ~thread!";
            }
        }

        bool isRunning() const { return m_Running.load(std::memory_order_acquire); }

        static thread * get() { return thread::m_Local; }

        static bool shouldBeRunning()
        {
            thread * Thread = thread::m_Local;
            return (Thread)
                ? Thread->m_ShouldBeRunning.load(std::memory_order_acquire)
                : true;
        }

        void stop() { m_ShouldBeRunning.store(false, std::memory_order_release); }
    };

    thread * thread::m_Local = NULL;

    class doStuff
    {
    private:
        int   m_X;
        float m_Y;
        char  m_Z;

    public:
        doStuff(int X, float Y, char Z)
            : m_X(X), m_Y(Y), m_Z(Z) {}

        void stuff(int X, float Y, char Z)
        {
            while (thread::shouldBeRunning())
            {
                std::cout << (X + m_X) << " " << (Y + m_Y) << " " << Z << m_Z << std::endl;
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
        }
    };

    class threadDoStuff : public thread
    {
    public:
        threadDoStuff(doStuff & Stuff, int X, float Y, char Z)
            : thread(&doStuff::stuff,
                         &Stuff,
                         X,
                         Y,
                         Z)
        {}
    };

    int main(int argc, char ** argv)
    {
        doStuff stuff(3, 2.0f, 'z');
        threadDoStuff stuffThread(stuff, 5, 1.0f, 'a');

        std::this_thread::sleep_for(std::chrono::seconds(30));

        return 0;
    }

MSVC 2013 吐出一条长长的错误链,最顶端是:

------ Build started: Project: test, Configuration: Debug Win32 ------
  main.cpp
c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(1149): error C2664: 'void (thread *,Function &&,doStuff *&&,int &,float &,char &)' : cannot convert argument 3 from 'doStuff *' to 'doStuff *&&'
          with
          [
              Function=void (__thiscall doStuff::* )(int,float,char)
          ]
          You cannot bind an lvalue to an rvalue reference

【问题讨论】:

  • 使用&amp;thread::run&lt;Function, Args...&gt;,您不再使用转发引用并强制将左值/右值引用传递给它。
  • 改用m_Thread(&amp;thread::run&lt;std::decay_t&lt;Function&gt;, std::decay_t&lt;Args&gt;...&gt;,
  • 好的,我知道这会导致函数的定义方式和调用方式之间存在差异,但这样做会导致error C2664: 'void (thread *,Function &amp;&amp;,doStuff *&amp;&amp;,int &amp;&amp;,float &amp;&amp;,char &amp;&amp;)' : cannot convert argument 6 from 'char' to 'char &amp;&amp;'

标签: c++ templates subclass variadic stdthread


【解决方案1】:

感谢 Jarod42 和 Piotr 提供部分解决方案,但我的问题最终通过升级到 MSVC 2015 得到解决。根据some comments,早期版本的 MSVC 在处理std::thread 构造函数参数时存在问题。虽然无论如何我都会升级,但我想在不升级的情况下找出问题所在,但这需要太多时间。另外我还没有运行代码,但至少它可以编译。

TLDR - 我没有找出原因,但可能是由于 MSVC,因为升级修复了它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-25
    • 2021-06-04
    • 2018-07-02
    • 2020-04-29
    • 1970-01-01
    • 2011-09-23
    相关资源
    最近更新 更多