C++ 如何欺骗boost::asio以允许仅移动处理程序

C++ 如何欺骗boost::asio以允许仅移动处理程序,c++,boost,c++11,boost-asio,move-semantics,C++,Boost,C++11,Boost Asio,Move Semantics,在RPC通信协议中,在调用一个方法之后,我将向调用者发送“done”消息。由于这些方法是以并发方式调用的,因此包含响应的缓冲区(astd::string)需要使用互斥锁进行保护。我正在努力实现以下目标: void connection::send_response() { // block until previous response is sent std::unique_lock<std::mutex> locker(response_mutex_);

在RPC通信协议中,在调用一个方法之后,我将向调用者发送“done”消息。由于这些方法是以并发方式调用的,因此包含响应的缓冲区(a
std::string
)需要使用互斥锁进行保护。我正在努力实现以下目标:

void connection::send_response()
{
    // block until previous response is sent
    std::unique_lock<std::mutex> locker(response_mutex_);

    // prepare response
    response_ = "foo";

    // send response back to caller. move the unique_lock into the binder
    // to keep the mutex locked until asio is done sending.
    asio::async_write(stream_,
                      asio::const_buffers_1(response_.data(), response_.size()),
                      std::bind(&connection::response_sent, shared_from_this(),
                                _1, _2, std::move(locker))
                      );
}

void connection::response_sent(const boost::system::error_code& err, std::size_t len)
{
    if (err) handle_error(err);
    // the mutex is unlocked when the binder is destroyed
}

boost::asio
不允许仅移动处理程序的原因是什么?

在Chris Kohlhoff对我提出的错误做出响应之前,这里有一个简单的解决方法:

template <typename F>
struct move_wrapper : F
{
    move_wrapper(F&& f) : F(std::move(f)) {}

    move_wrapper(move_wrapper&&) = default;
    move_wrapper& operator=(move_wrapper&&) = default;

    move_wrapper(const move_wrapper&);
    move_wrapper& operator=(const move_wrapper&);
};

template <typename T>
auto move_handler(T&& t) -> move_wrapper<typename std::decay<T>::type>
{
    return std::move(t);
}
shared_ptr<mutex> lock(mutex & m)
{
    m.lock();
    return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock));
}
模板
结构移动包装器:F
{
move_包装器(F&&F):F(std::move(F)){
move_wrapper(move_wrapper&&)=默认值;
move_wrapper&operator=(move_wrapper&&)=默认值;
移动包装器(常量移动包装器&);
move_wrapper&运算符=(const move_wrapper&);
};
模板
自动移动\u处理程序(T&&T)->移动\u包装器
{
返回std::move(t);
}
包装器声明了一个复制构造函数,诱使asio的机器提交,但从未定义它,因此复制将导致链接错误

现在我们终于可以做到这一点:

std::packaged_task<int()> pt([] {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
});
std::future<int> fu = pt.get_future();

boost::asio::io_service io;
io.post(move_handler(pt));
std::thread(&boost::asio::io_service::run, &io).detach();

int result = fu.get();
assert(result == 42);
std::打包任务pt([]{
std::this_thread::sleep_for(std::chrono::seconds(1));
返回42;
});
std::future fu=pt.get_future();
boost::asio::io_服务io;
io.post(移动处理器(pt));
std::thread(&boost::asio::io_service::run,&io).detach();
int result=fu.get();
断言(结果=42);

这里有一个更简单的解决方法:

template <typename F>
struct move_wrapper : F
{
    move_wrapper(F&& f) : F(std::move(f)) {}

    move_wrapper(move_wrapper&&) = default;
    move_wrapper& operator=(move_wrapper&&) = default;

    move_wrapper(const move_wrapper&);
    move_wrapper& operator=(const move_wrapper&);
};

template <typename T>
auto move_handler(T&& t) -> move_wrapper<typename std::decay<T>::type>
{
    return std::move(t);
}
shared_ptr<mutex> lock(mutex & m)
{
    m.lock();
    return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock));
}
请注意,由于
shared\u ptr
能够隐藏类型信息,因此
shared\u lock
未在
mutex
类型上模板化


它的成本可能会更高,但它也有一些优势(接收器可以使用
共享锁
,您可以向它传递一个可升级、共享、唯一的锁、范围保护,基本上是任何基本的可时钟或“更好”的锁)

这可能是因为它比C++11旧,他们没有时间/精力来修复它。看看最近的
boost
中是否存在这种情况,如果是,请提交一个bug!Hiárton;)乍一看,我想这仅仅是因为它还没有像许多其他boost库一样针对C++11进行更新。悲哀的现状:(什么编译器和boost的哪个版本?boost.Asio的更高版本确实支持。@SamMiller:我使用了苹果最新Xcode(4.6.2)和boost 1.53附带的叮当声。你链接到的页面声明如下:“但是,处理程序类型仍然需要是可复制构造的。”这正是我的问题:为什么?@Massa我已经提交了一个bug,这里它是供参考的:没错,这比我原来的
共享锁
包装器要短,尽管开销更大,正如你所写的那样。不过,最好的解决方案是IMHO通过声明复制构造函数和复制赋值操作符来假装你的处理程序是可复制的。由于asio从未实际使用它们,因此链接成功。在任一版本中,如果共享\u ptr构造函数抛出(例如,由于控制块分配,std::bad \u ALOC),互斥锁将不会被解锁。最好使用唯一的锁。我想直接将其移动到删除器中,但愚蠢且毫无意义,删除器必须是可复制的,因此:std::unique_lock l(m);shared_ptr result{&m,mem_fun(&mutex::unlock)};l.release();return result;@ArneVogel没有分配。看看吧?在你的草图中,你也在做同样的事情,但是在返回之前你解锁了互斥锁。你完全没有抓住问题的关键吗?在Boost中有没有针对这个问题的结构修复的更新?如果没有,你能发布一个开放缺陷的引用吗?看看并
shared_lock lock(m);