APP下载

基于索引的类复合成员访问方式的探讨

2014-12-22曹大有卢中宁

湖南师范大学自然科学学报 2014年5期
关键词:编译器歧义编程

曹大有,卢中宁

(郧阳师范高等专科学校计算机科学系,中国十堰 442000;2.郑州轻工业学院计算机与通信工程学院,中国郑州 450002)

模板元编程(Metaprogramming)指的是高阶编程,它运行在编译期.作为一种高阶C++编程技术,C++强大的模板机制赋予了模板在编译期的运算能力,模板元编程突出了编译期在整个程序构建和运行过程中的地位,努力将计算从运行期提前至编译期,它不但有效地防止程序错误被传播到运行期,而且能够实现以静态代码控制动态代码的目标,使计算尽可能完成于编译期的同时也提高了最终程序的运行性能.

MPL(Meta-Programming Library)是由David Abrahams 和Aleksey Gurtovoy 为方便模板元编程而开发的库,2003年被Boost 吸纳为其中的一员,此后又历经一些重大修改,目前已经相当完善.MPL 的出现是C++模板元编程发展中的一大创举,它提供了一个通用、高层次的编程框架,包括了序列、迭代器、算法、元函数等组件,具有高度的可重用性,提高了模板元编程的效率,使模板元编程的应用范围得到相当的扩展.

C++模板元编程诞生于十多年前,最初的研究方向是编译期数值计算,后来的实践发展证明,此项技术在类型计算领域也可以释放出巨大能量.现在模板元编程主要用处在于:数据计算、解开循环、类型处理和自动代码生成.类复合[1]就是一种自动代码生成技术,它通过通过模板元编程技术在C++的编译期由指定的类型序列和细粒度的template parameters 来创建类结构的编程技术,它的基本构想是以类型序列作为代码生成机制,驱动编译器为我们自动生成代码.但在对类型成员的访问方法上,文[1]中提出了通过显式的类型成员限定来访问value 的方法,这里则提出通过索引确定类型成员,从而进一步访问类型成员value 的方法,解决了模棱两可(歧义)的现象.

1 类复合的原理

1.1 类复合原理的介绍

类复合就是将类型序列中的每一个类型套用于一个由用户提供的基本template parameters 身上.这样就可以细粒度的步骤反复使用它来在程序的编译期让编译器自动创建类结构.

例如,为了生成一个struct,其成员具有由“一个类型序列给定的”类型,细粒度的template parameters 为:

这样我们就可以应用mpl 的fold 算法来构建类结构.

生成一个generated 对象时,其类型结构为:

上面展示的每一个store 特化都表示继承结构中的一层,它们分别包含一个成员,其类型为member_types 中的类型之一.层次结构见图1所示.

1.2 类复合中成员的访问方式

实际上使用这种方式复合的类可能不是很好使用,除非它们被小心地加以创建.因为尽管generated 对象实际上包含member_types 中的每一个类型成员,但它们仍很难被访问.最明显的问题是它们都称为value,除第一个外,我们无法直接访问其他任何一个,因为其余的都被继承结构的层次(layers)掩盖了.对于这种重复没有任何办法,这是在应用类复合(class composition)时一个无法更改的实事,因为尽管可以很容易地产生成员类型(member types),但没有什么办法来使用模板产生成员名字(member names).

一般很难去访问一个给定类型的value 成员,即便它转换成一个适当的基类,这是因为每一个store 特化都派生于它的第二个参数.这样当要访问存储在generated 中的long 的值时,必须用以下方式来访问:

也就是说访问store 的任何成员都需要知道原始序列中在它的类型之前的所有类型.一种简化是让编译器的函数实参推导(function argument deduction)机制做推断基类链(base class chain)工作:

这样对generated 中long 值的访问可以通过下方式来进行:

long& y=get〈long〉(generated).value;

在上面语句中:get 的第一个模板实参被限制为long,从而有效的函数参数变为store〈long,U〉const&,它匹配包含一个long 成员的generated 基类.

1.3 成员访问方式的缺点

这样也有一个缺点:当member_types 中内含重复类型时,就不能使用上面所提供的get 模板函数了.

看看下面这个member_types:

mpl::vector〈short[2],long,long,char*,int〉member_types;

现在member_types 中有两个long 类型.而同样的类生成过程:

所生成的类generated 中也有两个类型为long 的value 成员,其generated 类型为:

层次结构见图2所示.

图1 generated 对象的层次结构图Fig.1 Hierarchical chart of generated object

图2 generated 对象的层次结构图Fig.2 Hierarchical chart of generated object

如果我们再对generated 对象调用:

long& y=get〈long〉(generated).value;

C++编译器就会抱怨出现模棱两可(歧义)情况,因为generated 最终继承了store〈long,U〉两次.编译器不能确定我们要访问的是哪一个long 的value 值.

所以需要一个“以索引选择member_types 中类型”的get 模板方法,而非通过型别名称.如果可以通过藉由member_types 中指定的位置求出每个类型成员,就可以克服上述模棱两可(歧义)的现象.

2 类复合中成员访问方式的改进

2.1 改进方式的设想

根据前述get 模板方法的设计思想,现在的想法是用int n 来代替class T.那么我们就要在编译期通过提供的int n 来确定出class T,通过Boost 的MPL 库提供的元函数,这些设想是完全可以实现的.

2.2 设想的实现

首先通过int n 构造出一个整型外覆器类型mpl::int_〈n〉.MPL 提供了一组外覆器模板,用于将整数值转换成多态元数据,它的模板定义为:

这里之所以要将int n 转换成mpl::int_〈n〉,是因为后面的元函数指定要用元数据.

然后通过元函数mpl::begin〈〉救出类型序列中第一个类型元素的前向迭代器,方法为:

mpl::begin〈member_types〉::type;

接着,通过元函数mpl::advance〈〉求出n 指定位置处类型的迭代器,方法为:

最后通过mpl::deref〈〉元函数求出n 指定位置处的类型即可,完整的元函数操作为:

当然上面全部操作也可以通过元函数mpl::at〈〉来简单实现,即通过:

mpl::at〈member_types,mpl::int_〈n〉〉::type;

求出n 指定位置处类型.

注意上述的n 为正值,相对于mpl::vector 向量member_types 求出是前向值,但根据generated 的生成过程:

它是反序的,若要考虑反序那么全部元函数可变成:

有了上述讨论,我们新的重载的get 模板方法就应该为:

而mpl::at〈member_types,mpl::int_〈n〉〉::type 则是在程序的编译期完成的.

2.3 实例应用

那么对于mpl 的vector 序列:

mpl::vector〈short[2],long,long,char*,int〉,以下方法调用则是求出第3 个类型long 的值:

get〈3〉(generated).value;

而get〈2〉(generated).value;

则是求出第2 个类型long 的值.注意:根据函数实参推导(function argument deduction)机制,我们可以根据int n 推导出typename mpl::at〈member_types,mpl::int_〈n〉〉::type,而generated 则用来确定class U.

即使这里出现了两个long 类型,但根本不会出现模棱两可(歧义)的现象,通过本文提供的get 彻底解决的类复合成员的访问问题.

3 结束语

模板元编程是C++中一种高级编程技术,它处于编译期,而类复合可以驱动C++的编译器在编译期自动生成所需的代码,同时也将类型计算尽量提前至编译期,减少了运行期出错的机率,提高了最终程序运行性能.本文通过以索引的方式来访问类复合对象中的成员值,解决了歧义现象,解决的类复合成员中的value值的访问问题.

[1]DAVID A.C++模板元编程[M].荣 耀,译.北京:机械工业出版社,2010:153-155.

[2]ANDREI A.C++设计新思维[M].侯 捷,於春景,译.武汉:华中科技大学出版社,2003:64-74.

[3]DAVID V,NICOLAI M J.C++template 中文版[M].陈伟柱,译.北京:人民邮电出版社,2004.

[4]HERBERT S.C++完全参考手册[M].4 版.北京:清华大学出版社,2004.

[5]王晓宇,钱红兵.基于UML 类图和顺序图的C++代码自动生成方法的研究[J].计算机应用与软件,2013,30(1):190-195.

[6]周 毅,顾进广,张晓龙,等.一种面向复合属性的自适应对象模型[J].计算机应用与软件,2008,25(11):137-139.

[7]徐静雯,周继恩,施跃跃,等.软件密集型系统的故障诊断技术研究[J].计算机应用与软件,2012,29(2):175-178.

[8]黄 山,陈昱松,王建伟,等.一种基于UML 与SDL 融合建模的组件系统测试方法[J].计算机应用与软件,2011,28(7):175-177,182.

[9]唐 峰,许第洪.SolidWorks 与Pro/Engineer 之间图形数据交换方式的研究[J].湖南师范大学自然科学学报,2011,34(1):37-42.

[10]刘 震,缪 力.基于动态调用图的Java 程序修改影响分析技术[J].湖南师范大学自然科学学报,2011,34(6):26-30.

[11]PLAUGER P J,STEPANOV A A,LEE M,et al.C++STL 中文版[M].王 昕,译.北京:中国电力出版社,2002.

[12]BLANCHETTE J,SUMMERFIELD M.C++GUI Qt 4 编程[M].闫锋欣,曾泉人,张志强,译.北京:电子工业出版社,2008.

[13]叶至军.C++STL 开发技术导引[M].北京:人民邮电出版社,2007.

[14]MATTHEW H A.泛型编程与STL[M].侯 捷,译.北京:中国电力出版社,2003.

[15]ANDREW K,BARBARA M.C++沉思录[M].黄晓春,译.北京:人民邮电出版社,2008.

猜你喜欢

编译器歧义编程
编程,是一种态度
元征X-431实测:奔驰发动机编程
编程小能手
纺织机上诞生的编程
基于相异编译器的安全计算机平台交叉编译环境设计
eUCP条款歧义剖析
English Jokes: Homonyms
“那么大”的语义模糊与歧义分析
通用NC代码编译器的设计与实现
寻求歧义研究的解释力度:从认知视角到社会视角——兼评《现代汉语歧义识别与消解的认知研究》