0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

讲解消息分发的一种编译期实现法

CPP开发者 来源:CppMore 2023-08-23 14:38 次阅读

今天讲消息分发的一种编译期实现法。

编程是一门非常依赖逻辑的学科,逻辑分为形式逻辑和非形式逻辑,编程就属于形式逻辑。形式逻辑指的是用数学的方式去抽象地分析命题,它有一套严谨的标准和公理系统,对错分明;而日常生活中使用的是非形式逻辑,它不存在标准和公理,也没有绝对的对与错。

根据哲学家大卫·休谟在《人性论》中对于观念之间连接的分类,我们能够把逻辑关系分成三大类:相似关系、因果关系、承接关系。相似关系表示两个组件结构相同,去掉其中一个组件,也只会使功能不够全面,并不会影响程序;因果关系表示两个组件之间依赖性极强,没有第一个组件,就没有第二个组件,第二个组件依赖于第一个组件;承接关系表示两个组件都是局部,只有组合起来,才能构成一个整体。

消息分发就属于因果关系,我们需要依赖 A,去执行 B,没有 A 就没有 B。同样属于因果关系的术语还有逻辑分派、模式匹配、定制点的表示方式等等,它们本质都是在描述一类东西,只是有时候侧重点不同。

条件关系也属于因果关系的范畴,是编程中逻辑最重的关系。试想没有if else,你还能写出多少程序?世界是复杂的,问题也是复杂的,因果关系必不可少。

消息分发,或称逻辑分派,就是一种简化条件关系表达方式的技术。它适用于存在大量因果的情境,此时若是使用原始的if else,则无法适应动态发展的世界。

C++ 中,最典型、也非常有用的一种方式就是采用map,因作为 key,果作为 value,因是标识符,果是回调函数。由于这种方式发生于运行期,所以也称为动态消息分发。

本文要讲的,是 C++20 才得以实现的另外一种方式,发生于编译期的静态消息分发技术。

称为消息分发,一般是在网络通信的情境下。正常情境下,程序是顺序执行的,所以完全可以使用if else来实现因果逻辑,因为组件与组件之间距离较近,属于同一模块;而网络情境下,一个组件可以瞬间跳跃到距离非常远的另一个组件,这两个组件甚至不在同一台设备上,一台设备可能在上海,另一台在北京,此时如何让这两个组件进行沟通?也就是说,A 组件里面的某个函数执行条件不满足,如何简单地跳到 B、C、D、E…… 这些组件的某个函数中去处理?这种远距离的程序因果逻辑,通过消息分发组件能够非常丝滑地表示。

消息分发的标识符一般采用字符串表示,到了 C++20 支持 string literal NTTP 才得以在编译期实现一套可用的相关组件。

因此首先,我们得实现一个 string literal 以在编译期使用。


		1template<std::size_tN> 2structstring_literal{ 3//strisareferencetoanarrayNofconstantchar 4constexprstring_literal(charconst(&str)[N]){ 5std::copy_n(str,N,value); 6} 7 8charvalue[N]; 9};
通过这种方式,我们定义了编译期能够使用的字符串组件,它能够直接当作模板参数使用。
然后,定义我们的分发器。

		1template  2structdispatcher{  3template  4constexprautoexecute_if(charconst*cause)const{  5if(C==cause)handler();  6}  7  8constexprautoexecute(charconst*cause)const{  9(execute_if(cause),...); 10} 11};
代码非常精简,分发器可以包含很多「因」,使用可变模板参数 Cs 进行表示。如果得到一个具体的「因」,我们需要找到对应的「果」,因此免不了遍历 Cs,借助 Fold expressions,一行代码优雅地搞定。通过 execute_if 来查找是否存在对应的「因」,也就是对比字符串是否相等,查找到则调用相应的「果」,也就是具体的处理函数 handler()
接着,需要定义一个默认的因果,即如果没有定义相应的处理函数时,所调用的一个默认处理函数。

		1//defaultimplementation 2template 3inlineconstexprautohandler=[]{std::cout<< "defaulteffect ";};
因为是模板参数,所以它能够处理所有的「因」。
通过特化,我们能够在任何地方,定义任何因果。比如:

		1//opt-incustomizationpoints 2template<>inlineconstexprautohandler<"cause1">=[]{std::cout<< "customizationpointseffect1 ";}; 3template<>inlineconstexprautohandler<"cause2">=[]{std::cout<< "customizationpointseffect2 ";};
默认版本和定制版本之间是相似关系,即便不提供定制版本,也会不影响程序的功能。
由于特化更加特殊,所以决议时会首先考虑这些因果对。但是,此时有巨大的重复,我们通过宏来自动生成重复代码:

		1#define_(name)template<>inlineconstexprautohandler<#name> 2 3//opt-incustomizationpoints 4_(cause1)=[]{std::cout<< "customizationpointseffect1 ";}; 5_(cause2)=[]{std::cout<< "customizationpointseffect2 ";};
现在定制起来就更加方便、简洁。 最后,具体使用。

		1intmain(){ 2constexprstring_literalcause_1{"cause1"}; 3constexprdispatcher"cause2","cause3">dispatch; 4dispatch.execute(cause_1); 5dispatch.execute("cause2"); 6dispatch.execute("cause3"); 7}
相比动态消息分发,这种方式有两个巨大的优势,其一是编译期,其二是定制时可以在任何地方。动态消息分发一般需要调用 dispatch.add_handler(cause, effect),因为是成员函数,所以限制了定制地方,必须得在对象所在模块,而静态消息分发这种全局定义特化的方式,则没有这种限制。
目前其实还存在两个问题,第一是 C == cause 并没有相应的比较操作符,第二是 dispatch.execute(cause_1) 并不能直接传递,因为 char const*string_literal 毕竟不是同一种类型。可以通过添加运算符重载和隐式转换来解决:

		1template<std::size_tN>  2structstring_literal{  3//...  4  5friendbooloperator==(string_literalconst&s,charconst*cause){  6returnstd::strncmp(s.value,cause,N)==0;  7}  8  9operatorcharconst*()const{ 10returnvalue; 11} 12 13//... 14};
			现在以上静态消息分发组件就能够正常使用了。完整的代码如下:

		1template<std::size_tN>  2structstring_literal{  3constexprstring_literal(charconst(&str)[N]){  4std::copy_n(str,N,value);  5}  6  7friendbooloperator==(string_literalconst&s,charconst*cause){  8returnstd::strncmp(s.value,cause,N)==0;  9} 10 11operatorcharconst*()const{ 12returnvalue; 13} 14 15charvalue[N]; 16}; 17 18//defaultimplementation 19template 20inlineconstexprautohandler=[]{std::cout<< "defaulteffect ";}; 21 22#define_(name)template<>inlineconstexprautohandler<#name> 23 24//opt-incustomizationpoints 25_(cause1)=[]{std::cout<< "customizationpointseffect1 ";}; 26_(cause2)=[]{std::cout<< "customizationpointseffect2 ";}; 27 28 29template 30structdispatcher{ 31template 32constexprautoexecute_if(charconst*cause)const{ 33if(C==cause)handler(); 34} 35 36constexprautoexecute(charconst*cause)const{ 37(execute_if(cause),...); 38} 39}; 40 41intmain(){ 42constexprstring_literalcause_1{"cause1"}; 43constexprdispatcher"cause2","cause3">dispatch; 44dispatch.execute(cause_1); 45dispatch.execute("cause2"); 46dispatch.execute("cause3"); 47}
短短数十行代码,便实现了一个威力强大的静态消息分发组件,This is modern C++。
审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3614

    浏览量

    93685
  • 字符串
    +关注

    关注

    1

    文章

    578

    浏览量

    20506
  • C++
    C++
    +关注

    关注

    22

    文章

    2108

    浏览量

    73618
  • 代码
    +关注

    关注

    30

    文章

    4779

    浏览量

    68519
  • 编译
    +关注

    关注

    0

    文章

    657

    浏览量

    32851

原文标题:编译期消息分发?C++20 已能优雅实现!

文章出处:【微信号:CPP开发者,微信公众号:CPP开发者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    一种高性能多通道通用DMA设计与实现

    为充分发挥异构多核DSP芯片的实时计算能力,设计并实现一种高性能多通道的通用DMA,该DMA最大支持64个通道的数据搬运,并支持维、二维、转置以及级联描述符等多种传输模式。芯片实测
    的头像 发表于 11-20 15:52 1064次阅读
    <b class='flag-5'>一种</b>高性能多通道通用DMA设计与<b class='flag-5'>实现</b>

    介绍一种使用WSL来编译nodemcu固件的方法

    本文将介绍一种使用WSL来编译nodemcu固件的方法。
    发表于 02-15 07:34

    如何去实现一种RK3308系统交叉编译的设计

    如何去实现一种RK3308系统交叉编译的设计?有哪些编译步骤?
    发表于 03-09 07:57

    一种用于反编译代码与源代码的比较算法

    现有反编译器产生的代码与对应的源代码之间存在差异,找到并理解差异有助于改进并完善反编译器的设计。该文给出一种适用于C 语言反编译代码与源代码的比较算法。该算法以语
    发表于 03-21 15:08 10次下载

    一种健壮的计算安全的分布式密钥分发方案

    对基于Decisional Diffie-Hellmen 假设计算安全的分布式密钥分发方案进行改进,在其基础上利用可验证秘密共享和知识证明,提出一种健壮的计算安全的分布式密钥分发方案,以抵抗主动
    发表于 06-17 09:39 22次下载

    一种分布式编译系统的设计与实现

    本文针对当前大型软件生成时间过长的问题,在MSBuild 生成引擎的基础上,提出并实现一种利用集群进行分布式编译的系统,以降低在MSBuild 平台上的产品每次编译所需要的时间
    发表于 01-15 14:16 18次下载

    一种6_25Gb_s带预加重结构的低压差分发送器_陈浩

    一种6_25Gb_s带预加重结构的低压差分发送器_陈浩
    发表于 01-07 22:14 1次下载

    一种EPCCL理论编译算法

    to knowledge compilation based on hyper extension rule).该算法适合难解类SAT问题的知识编译,也是一种可并行的知识编译算法.研究了如何
    发表于 12-26 10:39 0次下载

    一种支持单双模式选择的SIMD编译优化算法

    和SIMD指令来实现,然而现有的编译框架无法对这些特殊的SIMD指令提供支持。由于BWDSPlOO拥有丰富的SIMD向量化资源,且其所运用的雷达数字信号处理领域对程序的性能要求极高,因此针对BWDSPlOO结构的特点,在传统Open64
    发表于 01-05 10:28 0次下载
    <b class='flag-5'>一种</b>支持单双模式选择的SIMD<b class='flag-5'>编译</b>优化算法

    一种短波软件无线电台的实现讲解

    一种短波软件无线电台的实现讲解说明。
    发表于 03-25 09:30 11次下载
    <b class='flag-5'>一种</b>短波软件无线电台的<b class='flag-5'>实现</b><b class='flag-5'>讲解</b>

    一种基于公交系统的任务差异化分发方法

    、参与数量、感知区域类型等带来的挑战。基于此,该文利用城市中公交载体的轨迹可预测、活动覆盖范围大、乘客节点自主聚集且交互时间有保证等优势,提出了一种基于公交系统的任务差异化分发方法。首先,利用泰森多边形的划
    发表于 03-31 09:11 4次下载
    <b class='flag-5'>一种</b>基于公交系统的任务差异化<b class='flag-5'>分发</b>方法

    FPGA_ASIC-一种改进的2D-DCT的FPGA实现

    FPGA_ASIC-一种改进的2D-DCT的FPGA实现(核达中远通电源技术有限公司招聘文员吗?)-该文档为FPGA_ASIC-一种改进的2D-DCT的FPGA实现
    发表于 09-16 10:35 4次下载
    FPGA_ASIC-<b class='flag-5'>一种</b>改进的2D-DCT的FPGA<b class='flag-5'>实现</b>

    电子学报第七一种可配置的CNN协加速器的FPGA实现方法》

    电子学报第七一种可配置的CNN协加速器的FPGA实现方法》
    发表于 11-18 16:31 15次下载

    如何通过poly实现C++编译多态

      而folly::poly出来的晚些,主要使用c++的新特性来实现相关的功能,依赖比较少,所以本文将更多的以poly的实现来分析编译
    的头像 发表于 12-05 09:10 640次阅读

    分享一种路由重分发的标准解决方案

    路由重分发技术在现网环境中是一种很常见的技术,所以其地位也非常重要。很多教程在在讲路由重分发的时候,只讲了重分发的操作却没有说现网环境中
    的头像 发表于 05-05 08:59 1064次阅读
    分享<b class='flag-5'>一种</b>路由重<b class='flag-5'>分发</b>的标准解决方案