如何在编译期进行拓扑排序,自动处理模块初始化依赖顺序。
|
模块之间的初始化和清理的顺序是很重要的。这个顺序应该可以根据各个模块之间的依赖关系求出。而且在绝大部分情况下,链接进工程的各个模块之间的依赖关系在编译期就可以确定出来。下面我们来讨论一下如何通过模板元编程构造一套方便的机制,让编译器自动帮你完成初始化和清理的排序工作。 为了方便大家理解这部分工作到底能够用于处理什么情况,这里先将实现后的用法说明一下: 假设我们有6个模块,分别叫做ModuleA,ModuleB,ModuleC,ModuleD,ModuleE,ModuleF。这6个模块各自有自己的初始化和清理函数(Init和Shutdown)。在使用这些模块的功能之前必须按照依赖关系进行排序然后初始化,在使用完成之后需要按照相反的顺序进行清理。如果这些顺序都由程序员自己手工控制无疑是容易出错的,为此可以利用模板元的帮助实现一个模块管理器帮助自动完成这项排序工作。 首先我们来看一下如何描述各个模块之间的依赖关系。在模块管理器中存在如下定义:
这是一个没有定义的模板结构,该结构的特化可以用于描述每个模块的初始化清理函数以及依赖关系。每个模块都必须各自根据自己的标签特化该结构,并写入自己的内容。 比如在模块A的头文件ModuleA.h中的模块描述代码如下:
模板特化利用了一个模块A的标签类(struct ModuleA)进行特化,并且由于模块A依赖于模块D,在dependencies中加入模块D的标签。注意标签是没有必要写出定义的,因为它只是用给编译器在编译器的一个标识,这样编译器根本不会为标签生成任何代码。 dependencies是一个类型链表(TypeList,详见下文)。该类型链表中就保存了A模块的依赖关系。后面两个静态函数Init和Shutdown分别描述了A模块的初始化和清理函数。 其他的5个模块都按这种方式在各自的头文件中写下自己的依赖项以及初始化和清理函数。 这样在使用的时候就可以方便的利用模板元的模块管理器进行排序,操作代码如下:
可以看到,这样就可以很方便在利用编译器帮你指定依赖顺序了。而且如果在依赖顺序关系中出现了依赖循环的情况,模板元也可以检测出来,并产生编译错误。这样就是一种很好的机制保证了初始化的清理工作的正确进行。 下面将详细描述一下整个依赖关系的拓扑排序是如何进行的: 首先建议参考下Modern C++ Design中对模板元编程的描述,特别是关于类型链表TypeList的实现,因为后面的内容需要对模板元编程有一定的认识。下面我会罗列实现编译期拓扑排序需要用到的东西。一些基本的部分和Modern C++ Design中的内容是相似的,但有一些地方不太相同。代码中的注释上对各个功能函数和结构的用途和算法都写得清楚。因此只在必要的地方进行解释。 为了方便模板元编程,我们首先需要一些基本的组件。这里用到的基本组件有: 将bool值映射成类型的模板:BoolToType
描述将一个空类型NullType,和判断一个类型是否为空类型的模板:IsNullType
根据一个编译期的bool值进行类型选择的结构Select:
然后就是最关键的部分,TypeList。下面拓扑排序中需要用到TypeList功能的代码:
值得注意的是其中实现了一些功能例如ReplaceAll和IsSubList是在Modern C++ Design中没有实现的。而IsSubList在拓扑排序中被用于检查某一项的所有依赖项是否都被解决了。 基础部分就是这些,下面将讲述拓扑排序的具体执行过程了。在拓扑排序的过程中,使用了两个类型链表:一个是排好序的类型链表,一个是还未排好序的类型链表。 排序流程是这样的: (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
