简体   繁体   English

如何将可变参数传递给 std::thread?

[英]How to pass variadic args to a std::thread?

I would like to use my own Thread implementation by wrapping the std::thread class from C++11 so I will be able handle exceptions like I want.我想通过包装 C++11 中的 std::thread 类来使用我自己的 Thread 实现,这样我就可以像我想要的那样处理异常。

Here is my wrap class:这是我的包装类:

#include <Types.hpp>
#include <thread>
#include <exception>
#include <functional>

class Thread
{
    private:

        std::exception_ptr exceptionPtr;
        std::thread thread;

    public:

        using Id = std::thread::id;

        using NativeHandleType = std::thread::native_handle_type;

        Thread() noexcept = default;
        Thread(Thread &&t) noexcept :
            exceptionPtr(std::move(t.exceptionPtr)),
            thread(std::move(t.thread))
        {
        }

        Thread &operator =(Thread &&t) noexcept
        {
            exceptionPtr = std::move(t.exceptionPtr);
            thread = std::move(t.thread);
            return *this;
        }

        template<typename Callable, typename... Args>
        Thread(Callable &&f, Args &&... args) :
            exceptionPtr(nullptr),
            thread([&](Callable &&f, Args &&... args)
            {
                try
                {
                    std::once_flag flag;
                    std::call_once(flag, f, args...);
                }
                catch (...)
                {
                    exceptionPtr = std::current_exception();
                }

            }, f, args...)
        {
            if (exceptionPtr != nullptr)
            {
                 std::rethrow_exception(exceptionPtr);
            }
        }

        bool joinable() const noexcept
        {
            return thread.joinable();
        }

        void join()
        {
            thread.join();
        }

        void detach()
        {
            thread.detach();
        }

        Id getId() const noexcept
        {
            return thread.get_id();
        }

        NativeHandleType nativeHandle()
        {
            return thread.native_handle();
        }

        static uint32_t hardwareConcurrency() noexcept
        {
            return std::thread::hardware_concurrency();
        }

        static void wait(Time t)
        {
            std::this_thread::sleep_for(t);
        }
};

It works pretty well if there is no argument:如果没有争论,它工作得很好:

Thread([&]() {  /* do something */ }).detach();

... but if I try to pass variadic arguments: ...但是如果我尝试传递可变参数:

Thread(&GUI::refreshTask, this, refreshDelay).detach();

... I get an error at compile time: ...我在编译时遇到错误:

buildroot-2014.02/output/host/usr/i586-buildroot-linux-uclibc/include/c++/4.8.2/functional: In instantiation of 'struct std::_Bind_simple)(std::chrono::duration >); buildroot-2014.02/output/host/usr/i586-buildroot-linux-uclibc/include/c++/4.8.2/functional: 在'struct std::_Bind_simple)(std::chrono::duration >); 的实例化中Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1(void (CRH::GUI:: )(std::chrono::duration >), CRH::GUI , std::chrono::duration >)>': buildroot-2014.02/output/host/usr/i586-buildroot-linux-uclibc/include/c++/4.8.2/thread:137:47: required from 'std::thread::thread(_Callable&&, _Args&&...) [with _Callable = CRH::Thread::Thread(Callable&&, Args&&...) [with Callable = void (CRH::GUI:: )(std::chrono::duration >); Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1(void (CRH::GUI:: )(std::chrono::duration >), CRH::GUI , std ::chrono::duration >)>': buildroot-2014.02/output/host/usr/i586-buildroot-linux-uclibc/include/c++/4.8.2/thread:137:47: 'std::thread 需要::thread(_Callable&&, _Args&&...) [with _Callable = CRH::Thread::Thread(Callable&&, Args&&...) [with Callable = void (CRH::GUI:: )(std::chrono::持续时间 >); Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1; Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1; _Args = {void (CRH::GUI:: &)(std::chrono::duration >), CRH::GUI const&, std::chrono::duration >&}]' /home/cyril/Documents/crh-2016/src/robot2/../core/Thread.hpp:72:30: required from 'CRH::Thread::Thread(Callable&&, Args&&...) [with Callable = void (CRH::GUI:: )(std::chrono::duration >); _Args = {void (CRH::GUI:: &)(std::chrono::duration >), CRH::GUI const&, std::chrono::duration >&}]' /home/cyril/Documents/crh -2016/src/robot2/../core/Thread.hpp:72:30: 来自'CRH::Thread::Thread(Callable&&, Args&&...) [with Callable = void (CRH::GUI:: )(std::chrono::duration >); Args = {CRH::GUI const, std::chrono::duration >&}]' src/core/GUI.cpp:90:57: required from here buildroot-2014.02/output/host/usr/i586-buildroot-linux-uclibc/include/c++/4.8.2/functional:1697:61: error: no type named 'type' in 'class std::result_of)(std::chrono::duration >); Args = {CRH::GUI const, std::chrono::duration >&}]' src/core/GUI.cpp:90:57: 从这里需要 buildroot-2014.02/output/host/usr/i586-buildroot- linux-uclibc/include/c++/4.8.2/functional:1697:61: 错误:'class std::result_of)(std::chrono::duration >) 中没有名为'type'的类型; Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1(void (CRH::GUI:: )(std::chrono::duration >), CRH::GUI , std::chrono::duration >)>' typedef typename result_of<_Callable(_Args...)>::type result_type; Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1(void (CRH::GUI:: )(std::chrono::duration >), CRH::GUI , std ::chrono::duration >)>' typedef typename result_of<_Callable(_Args...)>::type result_type; ^ buildroot-2014.02/output/host/usr/i586-buildroot-linux-uclibc/include/c++/4.8.2/functional:1727:9: error: no type named 'type' in 'class std::result_of)(std::chrono::duration >); ^ buildroot-2014.02/output/host/usr/i586-buildroot-linux-uclibc/include/c++/4.8.2/functional:1727:9: 错误:'class std::result_of 中没有名为'type'的类型)( std::chrono::duration >); Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1(void (CRH::GUI:: )(std::chrono::duration >), CRH::GUI , std::chrono::duration >)>' _M_invoke(_Index_tuple<_Indices...>) Args = {CRH::GUI const, std::chrono::duration >&}]::__lambda1(void (CRH::GUI:: )(std::chrono::duration >), CRH::GUI , std ::计时::持续时间>)>' _M_invoke(_Index_tuple<_Indices...>)

It could be a little clearer... but it will be too demanding for GCC.它可能会更清楚一点......但它对 GCC 的要求太高了。

Any idea how to fix this issue?知道如何解决这个问题吗?

Solution解决方案

#include <Types.hpp>
#include <thread>
#include <exception>
#include <functional>

class Thread
{
    private:

        std::exception_ptr exceptionPtr;
        std::thread thread;

    public:

        using Id = std::thread::id;

        using NativeHandleType = std::thread::native_handle_type;

        Thread() noexcept = default;
        Thread(Thread &&t) noexcept :
            exceptionPtr(std::move(t.exceptionPtr)),
            thread(std::move(t.thread))
        {
        }

        Thread &operator =(Thread &&t) noexcept
        {
            exceptionPtr = std::move(t.exceptionPtr);
            thread = std::move(t.thread);
            return *this;
        }

        template<typename Callable, typename... Args>
        Thread(Callable &&f, Args &&... args) :
            exceptionPtr(nullptr),
            thread([&](typename std::decay<Callable>::type &&f, typename std::decay<Args>::type &&... args)
            {
                try
                {
                    std::bind(f, args...)();
                }
                catch (...)
                {
                    exceptionPtr = std::current_exception();
                }

            }, std::forward<Callable>(f), std::forward<Args>(args)...)
        {
        }

        bool joinable() const noexcept
        {
            return thread.joinable();
        }

        void join()
        {
            thread.join();

            if (exceptionPtr != nullptr)
            {
                std::rethrow_exception(exceptionPtr);
            }
        }

        void detach()
        {
            thread.detach();
        }

        Id getId() const noexcept
        {
            return thread.get_id();
        }

        NativeHandleType nativeHandle()
        {
            return thread.native_handle();
        }

        static uint32_t hardwareConcurrency() noexcept
        {
            return std::thread::hardware_concurrency();
        }

        static void wait(Time t)
        {
            std::this_thread::sleep_for(t);
        }
};

Callable and Args are forwarding references, thus template argument deduction can make them either lvalue references or plain types, depending on the value category of argument expressions. CallableArgs是转发引用,因此模板参数推导可以使它们成为左值引用或普通类型,具体取决于参数表达式的值类别。

This means that when you reuse the deduced types in the declaration of a lambda:这意味着当您在 lambda 声明中重用推导的类型时:

thread([&](Callable&& f, Args&&... args)

reference collapsing comes into play and, for an lvalue argument refreshDelay , Args becomes an lvalue reference.引用折叠开始起作用,对于左值参数refreshDelayArgs成为左值引用。

However, std::thread stores decayed-copies of the arguments it receives, and then it moves from its internal storage to an actual handler, turning the stored objects into xvalues.然而, std::thread存储它接收到的参数的衰减副本,然后它从其内部存储移动到实际的处理程序,将存储的对象转换为 xvalues。 This is what the error tells you: the handler is not callable with the arguments the thread tries to pass in.这就是错误告诉您的内容:无法使用线程尝试传入的参数调用处理程序。

Instead, you can implement it as follows:相反,您可以按如下方式实现它:

template <typename Callable, typename... Args>
Thread(Callable&& f, Args&&... args)
    : exceptionPtr(nullptr)
    , thread([] (typename std::decay<Callable>::type&& f
               , typename std::decay<Args>::type&&... args)
            {
                // (...)
            }
            , std::forward<Callable>(f), std::forward<Args>(args)...)
{
    // (...)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM