首页 百科知识 通用程序设计

通用程序设计

时间:2022-10-16 百科知识 版权反馈
【摘要】:一个比较通用的版本是带一个参数:提高程序的通用性与OOP的目标分离内容相一致。因此OOP语言都具有用于提高代码通用性的相关技术,C++语言是模板而SystemVerilog是参数化类。上文的堆栈代码通用性不高,堆栈尺寸固定为20,堆栈中存储的数据类型固定为int类型。为提高stack通用性而做的另一个改变是使用traverse_init和traverse_next代替dump(),dump()依赖于编译时才能确定的堆栈成员类型。

3.5 通用程序设计

内容分离暗含了每个内容只需描述一次,代码复制则违背了该原则。实际中的很多问题都很相似,解决问题需要的代码也基本相似但不等同。面对此情况,第一想法就是试图利用代码相似的优点写出能解决尽可能多的问题的代码。这就导致通用代码即高度参数化代码的出现,它可轻易地在各种情况下重用。

在编译或者运行时提供通用代码详细内容,而不是用固定的代码。最常见的通用代码就是参数化函数。参照下面固定(非参数化)冗长的函数doubler()。

img74

该函数完成3的倍数运算,返回6。它的返回值固定为3的倍数,让人索然无味。一个比较通用的版本是带一个参数:

img75

该新版本的doubler()函数,将任何输入参数值翻倍,例如:

img76

新版doubler()函数明显比旧版函数更加通用。

提高程序的通用性与OOP的目标分离内容相一致。因此OOP语言都具有用于提高代码通用性的相关技术,C++语言是模板而SystemVerilog是参数化类。利用它们,可像数值的参数化一样参数化数据类型。如下例:

img77

img78

参数化类maximizer含有函数max(),该函数返回两个数值中的最大值。在C++里,可用模板构造相同的类。

img79

SystemVerilog语言和C++语言在实现maximizer上有个细小但很重要的不同。SystemVerilog仅支持语言本身内嵌的数据类型。如果T是类,则传入max()的a和b将是类对象的指针,而比较两个相关的类指针是毫无意义的。而C++版本则支持operator>()中定义的所有类和数据类型。如果不定义,则会导致编译错误。

为让maximizer能返回两个类对象中的较大值,需在每个类中都定义对象比较的方法,代码如下:

img80

代码假设类型参数T是类,而不是内嵌的数据类型如int或者real,另外还假设T内含有函数comp()用于T本身与另外实体的比较。AVM库中含有一个叫做avm_in_order_comparator #(T)的参数化组件,它常被用于事务流之间的比较,这将在第7章详细介绍。它有两个子类,一个是avm_in_order_builtin_comparator#(T),用于比较内嵌类型的数据流;另外一个是avm_in_order_class_comparator#(T),用于比较类数据流。需要两个顺序比较器的原因和需要两个maximizers的原因一样,都是因为SystemVerilog不支持既能对类操作又能对内嵌类型操作的运算符。

上文的堆栈代码通用性不高,堆栈尺寸固定为20,堆栈中存储的数据类型固定为int类型。下面代码将这些固定属性转化为参数化属性,提高了通用性。

img81

img82

通用stack类是带有堆栈存储对象类型的参数化类,堆栈存储对象类型由参数T限定。由于类型为T的对象不会被直接操作,因此T既可以是类也可以是内嵌类型。之前类中使用int作为堆栈类型的地方,现在都改为T。如修改后的push()函数就带有类型参数T。类的参数,如T,均是编译参数,这意味着参数值在编译时确定。例化带有特定参数值的类叫做具体化(specialization)。如具体化带有特定参数值的stack #(T):

stack #(real) real_stack;

该语句使用real作为堆栈存储对象的类型,例化了stack。

堆栈尺寸不再固定为20,而是由构造函数的参数决定,因此堆栈需存储于动态数组中。与T不同的是,size是运行参数,它的值在程序运行时才能确定,所以能同时构造多个尺寸不同的堆栈。

img83

big_stack和little_stack类型相同,都由stack #(T)具体化而来,然而例化时尺寸参数却并不相同。

为提高stack通用性而做的另一个改变是使用traverse_init和traverse_next代替dump(),dump()依赖于编译时才能确定的堆栈成员类型。成员类型可能是int类型,也可能是一个包含多成员的复杂类,这无法确定。但无论成员是什么类型,都需作堆栈遍历并格式化每个成员。

因此为了保证stack #(T)的通用性,必须抵制住需要依赖堆栈成员类型的所有尝试。

dump()遍历堆栈所有成员并按序打印,与之相反,traverse_init()设置一个指向堆栈顶部的遍历指针(tp),traverse_next()返回当前成员(被tp指定)并将tp减一。堆栈会维护遍历状态信息,当调用traverse_init()时,该信息被复位。

通过提高stack #(T)的通用性,消除对固定类型和尺寸的依赖,增加了该组件的重用性。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈