c++

模板多态

Posted on April 5, 2013

需求

jack 正在实现一个 avhttp , 并且也已经相当的可用了。 他在实现http协议的时候,就对 HTTPS 和 HTTP 的实现做了一个相当重要的决定 —– HTTPS和HTTP必须是统一的代码。

那么,要优雅的做到这一点,显然只有一个办法:多态。

但是很不幸的是,SSL 需要 asio::ssl::ssl_stream , 而 TCP 则用的是 asio::ip::tcp::socket。于是 jack 想到了boost::variant。 这是一个支持 非POD成员作为一个 unin 的库解决方案。

很不幸,即便如此, SSL 和 TCP 仍然有相当多的代码不能公用。然后对于代理服务器的支持, SSL 和 TCP 就更不能统一处理。

依然要相当多的条件分支。

就不能有一个更干净的办法么?

分层实现

于是我提出了将 avhttp 中 HTTP 请求过程分两层实现,一个是 DNS查询、TCP 链接的发起、代理服务器握手、SSL 握手等一系列的步骤。 也就是说,在真正的发起一次 HTTP 请求之前的一系列操作将其归类到第一层。接着,是第二层,就是真正的 HTTP 协议处理了。

那么第一层,无论是 HTTPS 还是 HTTP, 几乎肯定都是和 TCP 打交道,只有最后一步 HTTPS 多了一个 SSL 握手, 而 HTTP 则不需要。 这样, 第一层,不论是 HTTPS 还是 HTTP,只有唯一一处条件判断选择 SSL 握手这个分支语句。

接着到了第二层,不论是 HTTPS 还是 HTTP 都是一模一样的处理流程。

而对于后续HTTP响应的读取,继续使用目前所用的 boost::variant 机制即可。

第二层的实现采用模板多态

很明显,第二层的实现需要多态。而使用虚继承的形式的多态显然在这里大材小用了。需要写 3 个类(一个基类,两个虚继承的派生类)来封装 asio 的操作,明显也是过于繁琐了。

那么,显然,虽然 asio::ssl::ssl_stream 和 asio::ip::tcp::socket 不是同一个类型,但是明显都支持一样的操作! 都支持 asio::async_read/write 操作,都拥有 async_read/write 成员函数!

对于这样的具有相同“语法”的类,要进行多态,实在是太容易 —— C++ 提供了强大的模板机制,何乐而不为呢!

于是,明显的,第二层的采用模板实现。只要第一层在最后结束的时候,依据 HTTPS 还算 HTTP 为第二层调用提供不同的模板参数即可!

结论

C++ 为我们提供了强大的工具,我们要做的就是为当前的需求寻找C++提供的最恰当的机制。而不需要像 C 那样一味的向语言妥协。

Comments