2017-01-06 9 views
1

Я хотел написать оболочку над ASIO повышения для реализации клиент/сервер tcp. Интерфейс boost ASIO действительно хорош, но причиной необходимости обертки является возможность заменить цикл событий чем-то другим. В нашем usecase нам нужно просто вызвать одну и ту же функцию обработчика для каждого асинхронного чтения, приложению не нужно передавать обработчик для каждого вызова asyncRead. Таким образом, это помогает зарегистрировать обработчик на один раз. Один из способов, которым я пробовал, - этоBoost ASIO с CRTP или PBCP или утиная печать

template <class Connection> 
struct TCPClient { // implements the interface with ASIO 

Connection *_connection; 

void setConnection (Connection *connection) 
{ 
    _connection = connection; 
} 

void asyncRead() 
{ 
    _socket.async_read_some(boost::asio::null_buffers(), 
       [this] (ErrorType err, unsigned a) { 

        if (_connection) _connection->handleRead(err); 
        if (!err) asyncRead(); 
       }); 
} 

};

я мог бы сделать подобное с CRTP

class MyConnection : public TCPClient<MyConnection> { 
    void readHandler (TCPClient::ErrType err) 
    { 
    } 
}; 

А в классе TcpClient asyncRead будет

void asyncRead() 
{ 
    _socket.async_read_some(boost::asio::null_buffers(), 
       [this] (ErrorType err, unsigned a) { 
        ((Connection *)this)->handleRead(err); 
        if (!err) asyncRead(); 
       }); 
} 

Это в случае полезно, поскольку время жизни TcpClient и подключение такое же.

Или PBCP

template <typename Connection> 
class TCPClient : public Connection { 

void asyncRead() 
{ 
    _socket.async_read_some(boost::asio::null_buffers(), 
       [this] (ErrorType err, unsigned a) { 
        Connection::handleRead(err); 
        if (!err) asyncRead(); 
       }); 
} 
}; 

Я не думаю, что на самом деле есть IS-A отношение ч/б TcpClient и подключение. Я смущен, если какая-либо из этих схем хороша. (Также мне интересно, почему ASIO не имеет схемы, где кеширует обработчик один раз и вызывает его каждый раз. По крайней мере, в случае чтения Async обычно нет контекста, который нужно вернуть. В нашем случае скорость чтения сообщений является самой большой проблемой и Boost ASIO копирует обработчик чтения каждый раз + распределение памяти для хранения действительно плохо. Поэтому, основываясь на результатах тестирования, возможно, придется изменить цикл событий на что-то пользовательское)

+2

Asio предоставляет крючки для управления памятью обработчика (см. [Распределение пользовательской памяти] (http://www.boost.org/doc/libs/1_63_0/doc/html/boost_asio.html#boost_asio.overview.core.allocation]). Asio разработан, чтобы быть основой для дальнейших абстракций, а парадигма _one-callback-for-operation_ упрощает реализацию других парадигм. Прежде чем писать специальный цикл событий, я настоятельно рекомендую рассмотреть [libuv] (https://github.com/libuv/libuv), который поддерживает тот же обратный вызов, который вызывается в любое время, когда данные становятся доступными. –

+1

Я никогда не слышал о PBCP. Что это? Google очень уверенно ничего не знает об этом. (Если бы я должен был расшифровать аббревиатуру от кода, я бы предположил «Parameterized Base Class Pattern»? Мы бы назвали это Mixin) – sehe

+0

@sehe. Да, это он. – MGH

ответ

0

В этом смысле я проделал определенную работу. В вашем базовом классе CRTP вы можете попытаться создать шаблон-параметризованный метод, который вызывает производный класс и устанавливает std::function, содержащий lamba, который необходимо передать в async_read/write.

Базовый класс:

template <class Connection> 
struct TCPClient { // implements the interface with ASIO 

std::function<void(const boost::system::error&) handler{}; 

void registerConnectionHandler(std::function<void(const boost::system::error&)> &&impl) 
{ 
    static_cast<MyConnection*>(this)->registerConnectionHandler(std::forward<decltype(impl)>(impl)); 
}  

void asyncRead() 
{ 
    _socket.async_read_some(boost::asio::null_buffers(), handler); 
} 

}; 

В производном классе:

class MyConnection : public TCPClient<MyConnection> { 
public: 
    void registerConnectionHandler(std::function<void(const boost::system::error&)> &&impl) 
    { 
     handler = std::move(impl); 
    } 
};  

Другой способ сделать это, чтобы реализовать обработчик в производном классе, без использования registerConnectionHandler с станд :: функция, которая, вероятно, является наилучшим способом для этого:

class MyConnection : public TCPClient<MyConnection> { 
public: 
    void registerConnectionHandler() 
    { 
     handler = [this](const boost::system::error &err) 
     { 
     // your stuff here 
     }; 
    } 
};