C++ boost::asio boost::asio::basic_socket_acceptor::async_accept() 的接受处理程序中的可变参数模板运算符

C++ boost::asio Variadic template operator in accept handler for boost::asio::basic_socket_acceptor::async_accept()

我正在尝试创建一个接受连接的异步 TCP 服务器。我将要使用如下所示的接受处理函数对象:

template <typename Function>
struct callback
{
   Function func_;

   callback()
   {
   }

   callback(Function&& f)
        : func_(std::forward<Function>(f))
   {
   }

   template <typename ...Args>
   void operator() (Args&& ...args)
   {
         func_(std::forward<Args>(args)...);
   }
};

我的服务器class:

#include <boost/bind.hpp>
#include <boost/asio.hpp>

#include <session.hpp>
#include <handler.hpp>
class server
{
private:
    typedef callback<boost::function<void(const boost::system::error_code&, session_ptr&)>> accept_handler;

    boost::asio::io_service& io_service_;
    tcp::acceptor acceptor_;
    accept_handler handler_;

public:
    server(boost::asio::io_service& io_service, short port)
        : io_service_(io_service),
          acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
    {
        session_ptr new_session = session_ptr(new session(io_service_));
        auto callback =  boost::bind(&server::handle_accept,
                                     this,
                                     boost::asio::placeholders::error,
                                     new_session);

        handler_ = accept_handler(std::move(callback));


        acceptor_.async_accept(new_session->socket(),
                               handler_);
    }

    void handle_accept(const boost::system::error_code& error, session_ptr new_session)
    {
        if (!error)
        {
            new_session->start();
            new_session.reset(new session(io_service_));

            acceptor_.async_accept(new_session->socket(),
                                   handler_);

        }
    }
};

但是当我尝试编译时出现以下错误:

错误:对“(boost::function&)>) (const boost::system::error_code&)”的调用不匹配 func_(std::forward(args)...);

所以我必须只使用满足 AcceptHandler 要求的处理程序

struct accept_handler
{
  ...
  void operator()(
      const boost::system::error_code& ec)
  {
    ...
  }
  ...
};

或者有使用重载可变参数模板 opreator() 的解决方案?

UPDATED After realizing the real mistake:

好消息是:只需更改一行即可轻松修复错误:

typedef callback<boost::function<void(const boost::system::error_code&, session_ptr&)>> accept_handler;

进入

typedef callback<boost::function<void(const boost::system::error_code&)>> accept_handler;

之前的定义完全错误,原因如下:

  • 不符合处理程序要求
  • 它也不匹配 bind 表达式:bind(&server::handle_accept, this, asio::placeholders::error, new_session)。请注意,它只有 1 个占位符 (asio::placeholders::error),因此它不可能使用 2 个参数

Live On Coliru

#include <boost/function.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

namespace ba = boost::asio;
using ba::ip::tcp;

struct session : std::enable_shared_from_this<session> {
    session(ba::io_service& svc) : _socket(svc) {}

    void start() {} // do something asynchronously
    tcp::socket& socket() { return _socket; }

    tcp::socket _socket;
};

using session_ptr = std::shared_ptr<session>;

template <typename Function>
struct callback
{
   Function func_;

   callback(Function&& f = {}) : func_(std::forward<Function>(f)) {
   }

   template <typename ...Args>
   void operator() (Args&& ...args) {
       func_(std::forward<Args>(args)...);
   }
};

class server
{
private:
    typedef callback<boost::function<void(const boost::system::error_code&)>> accept_handler;

    ba::io_service& io_service_;
    tcp::acceptor acceptor_;
    accept_handler handler_;

public:
    server(ba::io_service& io_service, short port)
        : io_service_(io_service),
          acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
    {
        session_ptr new_session = session_ptr(new session(io_service_));
        handler_ = accept_handler(bind(&server::handle_accept, this, ba::placeholders::error, new_session));

        acceptor_.async_accept(new_session->socket(), handler_);
    }

    void handle_accept(const boost::system::error_code& error, session_ptr new_session)
    {
        if (!error)
        {
            new_session->start();
            new_session.reset(new session(io_service_));

            acceptor_.async_accept(new_session->socket(),
                                   handler_);

        }
    }
};

int main() {
}

简化:删除冗余包装

我在这里唯一要注意的是,none callback<>accept_handler 有任何用处:

Live On Coliru

class server
{
private:
    ba::io_service& io_service_;
    tcp::acceptor acceptor_;

public:
    server(ba::io_service& io_service, short port)
        : io_service_(io_service),
          acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
    { 
        do_accept();
    }

    void do_accept() {
        session_ptr new_session = session_ptr(new session(io_service_));
        acceptor_.async_accept(new_session->socket(), bind(&server::handle_accept, this, ba::placeholders::error, new_session));
    }

    void handle_accept(const boost::system::error_code& error, session_ptr new_session)
    {
        if (!error) {
            new_session->start();
            do_accept();
        }
    }
};

更简单:捕获 Lambda

您可以同时不使用绑定、占位符和 handle_accept 成员:

Live On Coliru

class server
{
private:
    ba::io_service& io_service_;
    tcp::acceptor acceptor_;

public:
    server(ba::io_service& io_service, short port)
        : io_service_(io_service),
          acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
    { 
        do_accept();
    }

    void do_accept() {
        session_ptr new_session = std::make_shared<session>(io_service_);

        acceptor_.async_accept(new_session->socket(), [this,new_session](const boost::system::error_code& error) {
            if (!error) {
                new_session->start();
                do_accept();
            }
        });
    }
};

旧答案

在第一次阅读时,我假设您 运行 陷入了我经常 运行 使用 Boost Asio 和多​​态 lambda 的经典陷阱。对不起。

确实,在可变情况下,概念检查无法验证处理程序要求。我的方法是禁用需求检查:

#define BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS 1

主要丢失的是不匹配时更友好的错误消息:

Asio 1.6.0 / Boost 1.47

  • Added friendlier compiler errors for when a completion handler does not meet the necessary type requirements. When C++0x is available (currently supported for g++ 4.5 or later, and MSVC 10), static_assert is also used to generate an informative error message. This checking may be disabled by defining BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS.

http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/history.html