使用CRTP或PBCP或鸭子打字来增强ASIO

时间:2017-01-06 01:53:48

标签: c++ boost boost-asio crtp duck-typing

我想在boost 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没有一个方案来缓存处理程序一次并且每次调用它。在Async读取的情况下,通常没有返回上下文。在我们的例子中,读取消息的速度是最大的问题, Boost ASIO每次复制读取处理程序+内存分配到存储都非常糟糕。因此根据测试结果我们可能需要将事件循环更改为自定义的东西。

1 个答案:

答案 0 :(得分:0)

我从这个意义上做了一些工作。 在您的CRTP基类中,您可以尝试创建一个模板参数化方法,该方法调用派生类并设置一个持有lamba的std::function,需要将其传递给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);
    }
};    

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

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