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

mar*_*n78 17 c++ boost boost-asio move-semantics c++11

在RPC通信协议中,在调用方法之后,我将"完成"消息发送回调用者.由于以并发方式调用方法,因此包含响应(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
}
Run Code Online (Sandbox Code Playgroud)

但是,这无法编译,因为boost::asio需要处理程序是CopyConstructible.

通过使用以下共享锁定器类而不是unique_lock:可以解决该问题(尽管不是非常优雅):

template <typename Mutex>
class shared_lock
{
public:
    shared_lock(Mutex& m)
    : p_(&m, std::mem_fn(&Mutex::unlock))
    { m.lock(); }

private:
    std::shared_ptr<Mutex> p_;
};
Run Code Online (Sandbox Code Playgroud)

什么是背后的原因boost::asio并不只允许布展处理程序?

mar*_*n78 13

直到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);
}
Run Code Online (Sandbox Code Playgroud)

包装器声明了一个复制构造函数,欺骗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);
Run Code Online (Sandbox Code Playgroud)

  • 看[这里](https://github.com/chriskohlhoff/asio/issues/13)和[这里](https://github.com/chriskohlhoff/asio/issues/69) (2认同)