Я хотел написать оболочку для надстройки ASIO для реализации клиент / сервер TCP. Интерфейс boost ASIO действительно хорош, но причина в том, что оболочка должна быть в состоянии заменить цикл обработки событий чем-то другим. В нашем сценарии использования нам просто нужно вызывать одну и ту же функцию-обработчик для каждого асинхронного чтения, приложению не нужно передавать обработчик для каждого вызова asyncRead. Так что это помогает зарегистрировать обработчик за один раз. Один из способов, который я попробовал, таков:
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 и Connection. Я запутался, если какая-то из этих схем хороша. (Также мне интересно, почему в ASIO нет схемы, в которой кешируется обработчик один раз и вызывается каждый раз. По крайней мере, в случае асинхронного чтения, как правило, отсутствует возвращаемый контекст. В нашем случае скорость чтения сообщений является самой большой проблемой и Ускорение ASIO-копирования каждый раз при чтении обработчика чтения + выделение памяти для хранения действительно плохо. Поэтому, основываясь на результатах теста, нам, возможно, придется изменить цикл обработки событий на что-то нестандартное)
Я проделал некоторую работу в этом смысле.
В вашем базовом классе CRTP вы можете попытаться создать параметризованный шаблонный метод, который вызывает производный класс и устанавливает станд :: функция удерживая лямбу, которую нужно передать 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 с std :: function, что, вероятно, является лучшим способом сделать это:
class MyConnection : public TCPClient<MyConnection> {
public:
void registerConnectionHandler()
{
handler = [this](const boost::system::error &err)
{
// your stuff here
};
}
};
Других решений пока нет …