I wanted to write a wrapper over the boost ASIO for a tcp client/server implementation. The interface of boost ASIO is really nice, but the reason to have to wrapper is to be able to replace the event loop with something else. In our usecase, we need to just need invoke the same handler function for every async read, the application doesn't need to pass the handler for every asyncRead call. So it helps to register the handler for once. One way I tried is like this -
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();
});
}
};
I could do the similar with CRTP
class MyConnection : public TCPClient<MyConnection> {
void readHandler (TCPClient::ErrType err)
{
}
};
And in TCPClient class the asyncRead will be
void asyncRead ()
{
_socket.async_read_some(boost::asio::null_buffers(),
[this] (ErrorType err, unsigned a) {
((Connection *)this)->handleRead(err);
if (!err) asyncRead();
});
}
This in case is helpful as the lifetime of the TCPClient and connection is same.
Or 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();
});
}
};
I dont think actually there is a IS-A relation b/w TCPCLient and Connection. I am confused if any of these scheme is good. (Also I wonder why ASIO doesnt have a scheme where is caches the handler once and calls it everytime. Atleast in case of Async read, normally there is no context to be returned. In our case, rate of reading messages is the greatest concern and Boost ASIO copying the read handler everytime + memory allocation to store is really bad. So based on test results we may have to change the event loop to something custom)
I have done some work in this sense. In your CRTP Base class you can try to create a template-parametrized method which calls the derived class and sets a std::function holding a lamba, which needs to be passed to the async_read/write.
Base class:
In the derived class:
Another way to do it is to implement the handler in the derived class, without using the registerConnectionHandler with a std::function, which is probably the best way to do it: