0

状況は次のとおりです。

を格納する Peer クラスとboost::asio::ip::tcp::socket、Peers のセット (ベクター/マップなど) を持つ Network クラスがあります。

とりあえず、Peer::listen()非同期読み取りを開始し、イベントの完了を にバインドしPeer::handle_incoming_messageます。asio::error::eofこれは、ピアが、離れたピアが切断されたかどうかを知るために を探す必要があることを意味します。

このメソッドで Peer オブジェクトを破棄する必要があるため、Peerを呼び出すことができませんNetwork::remove_peer()(実際には試していませんが、それremove_peerを呼び出した Peer オブジェクトが破棄された場合、から戻るとクラッシュするか何かになると思います)。だから今私がやっていることは、Peer オブジェクトの状態を に設定することだけです。PEER_STATE_DEADネットワークのセット内のすべてのピアを定期的にチェックし、状態を持つものをすべて削除しますPEER_STATE_DEAD

私は Network にhandle_incoming_message([...], Peer* p)メソッドを持たせたいと思っています。このメソッドは実際には が発生したかどうかのみをチェックし、発生していない場合は に指定されたバッファから読み取るeof呼び出しを行います。p->receive()async_read

Network::handle_incoming_message([...], this)したがって、理想的には、Peer クラスには、構築時 (またはその直後)に初期化される ListenHandler オブジェクトがあります。問題は、このセットアップでコンパイルできないようです。

簡略化されたコード:

通信網:

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]

(error_code、bytes_transferred)でReadHandlerコンストラクターを使用しようとしていることを理解しています。

私はこの質問を読みましたが、バインドは「[もの]を渡さないほど賢い」ようですが、それは、読み取りハンドラーを他のクラスを受け入れる別のクラスから非静的メソッドにバインドする(吸い込む)方法がないことを意味しますか? error_code、bytes_transferred 以外のパラメータ?

(Handler typedef を void(error_code, bytes_transferred, Peer*) に変更しようとしましたが、どちらも機能しませんでした)

あるいは、これのいずれかが悪い設計の悪臭を放っているように思われる場合は、物事をより適切に処理する方法について提案を受け付けています. bind()への呼び出しの中にへの呼び出しがないことが問題の原因である場合は、async_read()ネットワーク呼び出しを行うことができると思いますasync_read()...

4

1 に答える 1

1

つまり、boost::function引数を 1 つだけ受け入れるように型を変更します。boost::asio::placeholders::errorピアが既にバインドされているため、バインドされた関数には不明な値が 1 つだけあります ( で示されます)。

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::bindして、 Read HandlerHandlerの要件を満たす型に適合させます。

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.Asio のRead Handlerboost::functionのタイプ要件を満たしていないため、エラーが発生しています。読み取りハンドラーは、次の場合に必要です。

  • CopyConstructible であること。
  • タイプの左辺値をconst boost::system::error_code最初の引数として受け入れます。
  • type の左辺値をconst std::size_t2 番目の引数として受け入れます。

boost::bindが直接使用されたときにこれが表面化しない理由boost::bindは、から返される未指定のファンクタの型operator()が、未指定の型の 2 つの引数を受け入れるためです。これらの引数は、バインディング プロセスでプレースホルダーが提供された場合にのみ、バインドされた関数に転送されます。それ以外の場合、余分な引数は黙って無視/破棄されます。

したがって、Boost.Asio は常にboost::system::error_codeandでハンドラーを呼び出しますstd::size_t。元のコードでは、ハンドラの型は次のとおりです。

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

このタイプは、読み取りハンドラー タイプの要件を満たしていません。Boost.Asio は、2 番目の引数を としてハンドラーを呼び出そうとします。その結果、とconst std::size_tの間の変換に失敗するコンパイラ エラーが発生します。std::size_t (long unsigned int)Peer*

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

でラップHandlerすることにより、読み取りハンドラーの要件を満たすようにファンクターをboost::bind適応させます。Handler

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

Boost.Asio は結果のファンクターをboost::system::error_codeandで呼び出しますが、ファンクターは、破棄されるため、提供するだけをstd::size_t呼び出します。このブログでは、引数の受け渡しと破棄について説明しています。Handlerboost::system::error_codestd::size_t


最小限の完全な例を次に示します。

#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();
}

そして出力:

ピアは: 0xbfb2a6d2
不正なファイル記述子
ピアは: 0xbfb2a6d2
于 2013-08-08T18:25:14.313 に答える