c++ - 将异步操作的处理程序绑定(bind)到另一个类的非静态成员

标签 c++ asynchronous bind handler boost-asio

情况是这样的:

我有一个 Peer 类,它存储一个 boost::asio::ip::tcp::socket,还有一个 Network 类,它有一组( vector/ map /任何)对等点。

目前,Peer::listen() 启动异步读取,将事件的完成绑定(bind)到 Peer::handle_incoming_message。这意味着 Peer 必须查找 asio::error::eof 才能知道远程节点是否已断开连接。

Peer 无法调用 Network::remove_peer() 因为我希望此方法销毁 Peer 对象(实际上还没有尝试过,但我想从 remove_peer 如果调用它的 Peer 对象已被销毁,则会崩溃或发生其他事情)。 所以现在我所做的就是将 Peer 对象的状态设置为 PEER_STATE_DEAD,并定期检查网络集中的所有 Peer 并删除所有状态为 PEER_STATE_DEAD 的 Peer。

我希望 Network 有一个 handle_incoming_message([...], Peer* p) 方法,它实际上只会检查 eof 是否被引发,如果没有,调用 p->receive(),它会从给定的缓冲区中读取 async_read

理想情况下,Peer 类将有一个 ListenHandler 对象,该对象将在构造时(或之后不久)初始化为 Network::handle_incoming_message([...], this)。问题是,我似乎无法使用此设置进行编译。

简化代码:

网络:

class Network: 
{
[...]
  void handle_incoming_message(const system::error_code& error,
                               Peer* emitter);
[...]
};

void Network::add_peer(shared_ptr<Peer> p)
{
  peers.push_back(p);
  p->start_listening(bind(&Network::handle_incoming_message, this,
                          asio::placeholders::error, p.get() ));
}

同行:

class Peer
{
public:
  typedef function<void (const system::error_code&, Peer*)> Handler;
  void start_listening(Peer::Handler _listen_handler);
  void listen();
[...]
private:
  Handler listen_handler;
  char last_message[MESSAGE_SIZE];
[...]
};

void Peer::start_listening(Peer::Handler _listen_handler)
{
  listen_handler = _listen_handler;
  set_state(PEER_STATE_CONNECTED);
  listen();
}

void Peer::listen()
{
  memset(last_message, 0, MESSAGE_SIZE);

  socket->async_read_some(asio::buffer(last_message), listen_handler);
}

void Peer::receive()
{
    cout << get_address() << ": " << last_message << endl;
    listen();
}

G++:

g++ -c -Wall -D DEBUG_ON -Iinclude -I/usr/include src/Peer.cpp
In file included from /usr/include/boost/asio.hpp:30:0,
                 from include/Peer.hpp:4,
                 from src/Peer.cpp:3:
/usr/include/boost/asio/basic_stream_socket.hpp: In instantiation of
    ‘void boost::asio::basic_stream_socket<Protocol, StreamSocketService>::async_read_some(const MutableBufferSequence&, const ReadHandler&)
    [with
      MutableBufferSequence = boost::asio::mutable_buffers_1;
      ReadHandler = boost::function<void(const boost::system::error_code&, Peer*)>;
      Protocol = boost::asio::ip::tcp;
      StreamSocketService = boost::asio::stream_socket_service<boost::asio::ip::tcp>]’:
src/Peer.cpp:30:69:   required from here
/usr/include/boost/asio/basic_stream_socket.hpp:785:57: error: invalid conversion from ‘long unsigned int’ to ‘Peer*’ [-fpermissive]
In file included from /usr/include/boost/function/detail/maybe_include.hpp:23:0,
                 from /usr/include/boost/function/detail/function_iterate.hpp:14,
                 from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:57,
                 from /usr/include/boost/function.hpp:64,
                 from include/Peer.hpp:5,
                 from src/Peer.cpp:3:
/usr/include/boost/function/function_template.hpp:754:17: error:
    initializing argument 2 of ‘boost::function2<R, T1, T2>::result_type boost::function2<R, T1, T2>::operator()(T0, T1) const
    [with
      R = void;
      T0 = const boost::system::error_code&;
      T1 = Peer*;
      boost::function2<R, T1, T2>::result_type = void]’ [-fpermissive]

我有点理解它正在尝试将 ReadHandler 构造函数与 (error_code, bytes_transferred) 一起使用。

我读过 this question并且它似乎 bind 是“足够聪明,不会传递 [stuff]”,但这是否意味着没有办法(inhales)将读取处理程序绑定(bind)到另一个类接受的非静态方法除error_code、bytes_transferred以外的其他参数?

(我尝试将 Handler typedef 更改为 void(error_code, bytes_transferred, Peer*) 但它也不起作用)

或者,如果其中任何一个看起来设计不佳,我愿意听取有关如何更好地处理事情的建议。如果问题是由于在调用 async_read() 时没有调用 bind(),我想我可以进行网络调用 async_read()...

最佳答案

简而言之,将 boost::function 类型更改为仅接受一个参数。绑定(bind)函数只有一个未知值(用 boost::asio::placeholders::error 表示),因为对等方已经绑定(bind):

typedef boost::function<void (const boost::system::error_code&, Peer*)> Handler;

变成:

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

初始化async_read_some操作时,使用boost::bindHandler适配为满足Read Handler要求的类型.

socket->async_read_some(asio::buffer(last_message), listen_handler);

变成:

socket->async_read_some(asio::buffer(last_message),
  boost::bind(listen_handler, boost::asio::placeholders::error));

更详细的说,错误是因为boost::function类型不符合Boost.Asio的Read Handler的类型要求.读取处理程序需要:

  • 可复制构造。
  • 接受 const boost::system::error_code 类型的左值作为其第一个参数。
  • 接受一个 const std::size_t 类型的左值作为它的第二个参数。

当直接使用 boost::bind 时,这不会出现的原因是从 boost::bind 返回的未指定仿函数类型有一个 operator( ) 接受两个未指定类型的参数。如果在绑定(bind)过程中提供了占位符,这些参数只会转发给绑定(bind)函数。否则,额外的参数将被默默地忽略/丢弃。

因此,Boost.Asio 将总是使用boost::system::error_codestd::size_t 调用处理程序。在原始代码中,处理程序类型为:

boost::function<void (const boost::system::error_code&, Peer*)>

此类型不符合读取处理程序类型要求。 Boost.Asio 尝试使用第二个参数作为 const std::size_t 调用处理程序,导致编译器错误导致 std::size_t (long unsigned int) 之间的转换失败> 和 Peer*

invalid conversion from ‘long unsigned int’ to ‘Peer*’

通过用 boost::bind 包装 Handler,它调整 Handler 仿函数以满足读取处理程序的要求。

boost::bind(listen_handler, boost::asio::placeholders::error);

尽管 Boost.Asio 将使用 boost::system::error_codestd::size_t 调用生成的仿函数,该仿函数将调用 Handler 仅提供 boost::system::error_code,因为 std::size_t 将被丢弃。这blog说明参数的传递和丢弃。


这是一个最小的完整示例:

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

class Peer {};

class Network
{
public:
  void handle_incoming_message(const boost::system::error_code& error,
                               Peer* peer)
  {
    std::cout << error.message() << "\n"
              << "Peer is: " << peer << std::endl;
  };
};

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


int main()
{
  Network network;
  Peer peer;
  std::cout << "Peer is: " << &peer << std::endl;

  // Create the handler, that accepts a single argument.
  Handler handler = 
      boost::bind(&Network::handle_incoming_message, &network,
                  boost::asio::placeholders::error,
                  &peer);

  // Create io_service and socket.
  boost::asio::io_service io_service;
  boost::asio::ip::tcp::socket socket(io_service);

  // Initialize operation, adapting Handler to meet Read Handler requirements.
  socket.async_read_some(boost::asio::null_buffers(),
    boost::bind(handler,
                boost::asio::placeholders::error));

  io_service.run();
}

输出:

Peer is: 0xbfb2a6d2
Bad file descriptor
Peer is: 0xbfb2a6d2

关于c++ - 将异步操作的处理程序绑定(bind)到另一个类的非静态成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18131571/

相关文章:

bind - 不应该绑定(bind)? '全局变量返回系统/单词上下文?

mysql - MySQL安装时绑定(bind)IP

c++ - 防止内存泄漏(具体情况)

opengl - glShaderSource的参数是什么意思?

ios - 从 Swift 函数中的异步调用返回数据

javascript - 如何使用 Node.js 和 Express 在 JavaScript 中通过 GET 请求更改页面?

c++ - 绑定(bind)类方法到线程

C++ 类型无法解析错误

c++ - 大数组上的段错误

c# - 如何聚合多个任务结果