工作中 C++ 泛型编程用的多吗?

工作中 C++ 泛型编程用的多吗?

这个问题请务必让我来一发!去年一个项目里用了8、9种方式,绝对是实战上值得拿出来和大家分享的一段经历!其实泛型并不是越多越好,我认为泛型会增加代码本身的复杂性(哪怕它能减少代码量),只是当我们面临具体问题,发现需要一些解耦的方案时,知道能有什么方法可以用就行了。

这个项目里,一套代码支持了:

纵向可以增加新的层次,横向也可以随便扩展新功能。

这都是用各种天花乱坠的泛型方式来实现的代码统一,有些实现方式系统来说也许不叫泛型编程,但目的都是为了动态可扩展地解耦合。

项目在这里↓↓↓ 然后我们开始看看为什么、和怎么用:

项目的wiki介绍过架构,今天终于有机会单独把泛型的做法拿出来写一下 ~~~>_<~~~

♦ 第一个问题:RPC框架希望支持两种最热门的IDL:protobuf和thrift,但大家的序列化函数不一样,怎么做到具体哪个IDL就调哪个函数呢?

【方法1:同名函数】

♦ 第二个问题:目前SPRC除了自己的sogou-std协议以外、还支持了tRPC和BRPC协议,其中tRPC协议是目前唯一一个开源实现。那么如何支持不同RPC应用层协议?

【方法2:模版】

我们知道,SRPC是基于Workflow做的RPC框架,一次异步网络交互就是一个任务,任务本身的行为是收发、序列化、压缩本身,而和具体什么协议是没有关系的,所以协议本身可以抽象为两个模版:REQ和RESP,则我们一个网络交互就是长这样的:

♦ 第三个问题:RPC协议里一般都会有一个框架相关的可扩展头部,我们称为meta,其可扩展是比如借用IDL自身的前后兼容能力实现的。那么不同meta的检查各协议不同,如何解决呢?

【方法3:虚函数】

然后各不同协议有不同的实现即可,虚函数非常常见和好使~

♦ 第四个问题:刚才说的不同压缩类型,如何根据compress type找到我要的压缩函数?

【方法4:函数指针】

这是最基本、最原始的泛型编程方式,很多上层的泛型其实也是借用函数指针来实现的。

这里分三步讲一下:全局留好函数指针、具体函数指针的实现、注册到全局。

1)我们支持gzip、zlib、snappy、lz4,这些压缩库有些是纯C代码,因此我们对每种类型都包成了一个CompressHandler:

2) 给snappy实现它的压缩/解压函数,内部调用snappy的api:

3) 然后,注册上去:

这样RPCMessage调用compress()的时候就可以根据压缩类型自动找到函数指针了~

♦ 第五个问题:如何支持跨协议呢?比如,对方发过来HTTP请求带上protobuf的内容(而不是二进制的SRPC协议),我照样可以用这套RPC框架做反序列化的事情。

【方法5:共同派生和交叉调用】

由于这里派生关系比较复杂,直接上图。

用过Workflow的小伙伴都知道,消息收发是每个消息各自实现的append()函数,所以Workflow中的HttpMessage::append()已经有现成的实现,可以帮我们收Http协议了。

只要我们对二进制协议按照二进制协议去实现append()把消息收完,然后单独的用户IDL数据部分大家都作为body拎出来,body在中间的SRPCHttpRequest/SRPCHttpResponse层做反序列化,则可以复用Workflow的消息解析Http的代码了(机智如我!

~~~~~~~~~~~~~~~~~~~~~~~~~

插播:所以到这里,我们就可以实现:同一份用户service实现,放到不同的协议server里混用了!!!这里以protobuf的service作为例子看一下:

~~~~~~~~~~~~~~~~~~~~~~~~~

下面的几种方式并没有那么通用,所以简单列举一下,不细展开。尤其是6和7,本质上是解决IDL的问题:用户定义的结构如何序列化/反序列化和生成api的问题。

♦ 第六个问题:不想用protobuf生成的默认service接口,原始接口太繁琐,希望生成一套满足Workflow任务流的service接口。

【方法6:自己做代码生成】解决IDL中的service和method

♦ 第七个问题:thrift的网络与序列和分离不如protobuf好,但解析thrift IDL不想链接thrift库,而是希望Workflow做网络收发、SRPC做IDL解析。

【方法7:自己做内存反射】解决IDL中的message

♦ 第八个问题:SRPC是基于Workflow异步框架的二次开发,而Workflow是回调函数模式的,SRPC有许多事情需要在Workflow和用户之间做,比如反序列化。

【方法8:接管用户回调】回调函数std::function本身就是可以自带上下文~

♦ 第九个问题:用户做二次开的可扩展功能如何支持?

【方法9:模块化插件】

用一个图来讲解一下模块化插件吧,这个思路和nginx的filter是类似的:一个请求/回复,我们作为框架态可以为开发者提供接口,让开发者做一些独立的请求处理模块,以实现鉴权、监控等事情。

~~~~~~~~~~~~~~~~~~~~~~~~~

写完回答发现又摸鱼了俩小时(Q_Q) 最后梳理一下这些泛型方法所做出来的层次和功能:

SRPC整体代码约仅有1万行,以上内容都包括在同一套代码里,解耦的时候就像在做一个艺术品一样去打磨,希望能用最精巧和最灵活的方式来实现层级之间的调用,并且可以以最小的代价增加每个层级中的新功能~~~

各位小伙伴对于这些做法有什么建议都欢迎多多交流哟!!!(^∇^)

————————

由于突然很多小伙伴点赞,让我受宠若惊 ///>~</// 因此我在改某些错别字的同时,顺带补充一下,这里介绍的方法并不是都是理论上的“泛型编程”,有些算是实践上广义的多态,但要解决的问题是一样的,所以才更加觉得值得与大家分享~~~拿着实际项目代码来交流也觉得会比较有诚意~希望这篇回答能够给大家看到与实践的结合,我们其实有许多种途径去追寻架构中的结构之美。