2013-08-08 2 views
0

Вот ситуация:Привязка обработчика для асинхронной операции для нестатического члена другого класса

У меня есть класс Peer, который хранит boost::asio::ip::tcp::socket и сеть класс, который имеет набор (вектор/карта/все).

На данный момент Peer::listen() начинает чтение асинхронного сообщения, связывая завершение события с Peer::handle_incoming_message. Это означает, что Peer должен искать asio::error::eof, чтобы узнать, отключен ли удаленный одноранговый узел.

Peer не может позвонить Network::remove_peer(), потому что я хочу, чтобы этот метод уничтожал объект Peer (на самом деле не пытался, но я думаю, что возвращение с remove_peer могло бы сбой или что-то, если объект Peer, который его назвал, был уничтожен). Итак, теперь все, что я делаю, устанавливается в состояние Peer-объекта PEER_STATE_DEAD, и периодически я проверяю все Peers внутри набора Network и удаляет все из них с состоянием PEER_STATE_DEAD.

Я хотел сеть, чтобы иметь 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())); 
} 

Peer:

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 и, кажется связать «достаточно умен, чтобы не пройти [вещи]», но это означает, что, что нет никакого способа (вдыхает) связать обработчик чтения в не статический метод из другого класса, принимающего другие параметры, кроме error_code, bytes_transferred?

(я попытался изменить ЬурейеЕ Handler к мочеиспусканию (error_code, bytes_transferred, Peer *), но это не сработало)

В качестве альтернативы, если это, кажется, воняет плохой дизайн, я открыт к предложениям о том, как лучше обращаться с вещами. Если проблема так или иначе происходит от не имея вызов bind() внутри вызова async_read(), я предполагаю, что я мог бы сделать вызов сети async_read() ...

ответ

1

Короче говоря, изменить тип boost::function только принимает один аргумент.Связана функция имеет только одно неизвестное значение (обозначается с boost::asio::placeholders::error), так как одноранговый узел уже связан:

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 адаптировать Handler к тип, отвечающий требованиям 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. A Обработчик для чтения требуется:

  • Be CopyConstructible.
  • Принять значение lvalue типа const boost::system::error_code в качестве первого аргумента.
  • Принять значение lvalue типа const std::size_t в качестве второго аргумента.

Причина этого не поверхностей, когда boost::bind используется непосредственно в том, что не определен тип функтор вернулся из boost::bind имеет operator() принимает два аргумента неопределенного типа. Эти аргументы передаются только связанной функции, если в процессе связывания были предоставлены заполнители. В противном случае дополнительные аргументы молча игнорируются/отбрасываются.

Таким образом, Boost.Asio будет всегда вызовите обработчик с boost::system::error_code и std::size_t. В исходном коде, тип обработчик:

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

Этот тип не соответствует требованиям типа Read Handler. Boost.Asio пытается вызвать обработчик со вторым аргументом как const std::size_t, в результате чего ошибка компилятора завершилась с ошибкой преобразования между std::size_t (long unsigned int) и Peer*.

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

К оберточному Handler с boost::bind, он адаптирует Handler функтора для удовлетворения требований пользователя Handler.

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

Хотя Boost.Asio будет вызывать результирующий функтор с boost::system::error_code и std::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
Смежные вопросы