通过CRTP或PBCP或鸭子打字来增强ASIO

Boost ASIO with CRTP or PBCP or duck-typing

本文关键字:增强 ASIO 鸭子 CRTP PBCP 通过      更新时间:2023-10-16

我想为tcp客户端/服务器实现在boost ASIO上编写一个包装器。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关系b/w TCPCLient和Connection。我不知道这些方案中是否有一个是好的。(我还想知道为什么ASIO没有一个is缓存一次处理程序并每次调用它的方案。至少在异步读取的情况下,通常不会返回上下文。在我们的情况中,读取消息的速率是最大的问题,Boost ASIO每次复制读取处理程序+存储内存分配真的很糟糕。因此,根据测试结果,我们可能不得不将事件循环更改为自定义)

我在这个意义上做了一些工作。在您的CRTP基类中,您可以尝试创建一个模板参数化方法,该方法调用派生类并设置一个std::函数,该函数包含一个需要传递给async_read/write的lamba。

基本类别:

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

另一种方法是在派生类中实现处理程序,而不使用带有std::函数的registerConnectionHandler,这可能是最好的方法:

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