曹大有,盧中寧
(鄖陽師范高等??茖W(xué)校計(jì)算機(jī)科學(xué)系,中國(guó)十堰 442000;2.鄭州輕工業(yè)學(xué)院計(jì)算機(jī)與通信工程學(xué)院,中國(guó)鄭州 450002)
模板元編程(Metaprogramming)指的是高階編程,它運(yùn)行在編譯期.作為一種高階C++編程技術(shù),C++強(qiáng)大的模板機(jī)制賦予了模板在編譯期的運(yùn)算能力,模板元編程突出了編譯期在整個(gè)程序構(gòu)建和運(yùn)行過程中的地位,努力將計(jì)算從運(yùn)行期提前至編譯期,它不但有效地防止程序錯(cuò)誤被傳播到運(yùn)行期,而且能夠?qū)崿F(xiàn)以靜態(tài)代碼控制動(dòng)態(tài)代碼的目標(biāo),使計(jì)算盡可能完成于編譯期的同時(shí)也提高了最終程序的運(yùn)行性能.
MPL(Meta-Programming Library)是由David Abrahams 和Aleksey Gurtovoy 為方便模板元編程而開發(fā)的庫,2003年被Boost 吸納為其中的一員,此后又歷經(jīng)一些重大修改,目前已經(jīng)相當(dāng)完善.MPL 的出現(xiàn)是C++模板元編程發(fā)展中的一大創(chuàng)舉,它提供了一個(gè)通用、高層次的編程框架,包括了序列、迭代器、算法、元函數(shù)等組件,具有高度的可重用性,提高了模板元編程的效率,使模板元編程的應(yīng)用范圍得到相當(dāng)?shù)臄U(kuò)展.
C++模板元編程誕生于十多年前,最初的研究方向是編譯期數(shù)值計(jì)算,后來的實(shí)踐發(fā)展證明,此項(xiàng)技術(shù)在類型計(jì)算領(lǐng)域也可以釋放出巨大能量.現(xiàn)在模板元編程主要用處在于:數(shù)據(jù)計(jì)算、解開循環(huán)、類型處理和自動(dòng)代碼生成.類復(fù)合[1]就是一種自動(dòng)代碼生成技術(shù),它通過通過模板元編程技術(shù)在C++的編譯期由指定的類型序列和細(xì)粒度的template parameters 來創(chuàng)建類結(jié)構(gòu)的編程技術(shù),它的基本構(gòu)想是以類型序列作為代碼生成機(jī)制,驅(qū)動(dòng)編譯器為我們自動(dòng)生成代碼.但在對(duì)類型成員的訪問方法上,文[1]中提出了通過顯式的類型成員限定來訪問value 的方法,這里則提出通過索引確定類型成員,從而進(jìn)一步訪問類型成員value 的方法,解決了模棱兩可(歧義)的現(xiàn)象.
類復(fù)合就是將類型序列中的每一個(gè)類型套用于一個(gè)由用戶提供的基本template parameters 身上.這樣就可以細(xì)粒度的步驟反復(fù)使用它來在程序的編譯期讓編譯器自動(dòng)創(chuàng)建類結(jié)構(gòu).
例如,為了生成一個(gè)struct,其成員具有由“一個(gè)類型序列給定的”類型,細(xì)粒度的template parameters 為:
這樣我們就可以應(yīng)用mpl 的fold 算法來構(gòu)建類結(jié)構(gòu).
生成一個(gè)generated 對(duì)象時(shí),其類型結(jié)構(gòu)為:
上面展示的每一個(gè)store 特化都表示繼承結(jié)構(gòu)中的一層,它們分別包含一個(gè)成員,其類型為member_types 中的類型之一.層次結(jié)構(gòu)見圖1所示.
實(shí)際上使用這種方式復(fù)合的類可能不是很好使用,除非它們被小心地加以創(chuàng)建.因?yàn)楸M管generated 對(duì)象實(shí)際上包含member_types 中的每一個(gè)類型成員,但它們?nèi)院茈y被訪問.最明顯的問題是它們都稱為value,除第一個(gè)外,我們無法直接訪問其他任何一個(gè),因?yàn)槠溆嗟亩急焕^承結(jié)構(gòu)的層次(layers)掩蓋了.對(duì)于這種重復(fù)沒有任何辦法,這是在應(yīng)用類復(fù)合(class composition)時(shí)一個(gè)無法更改的實(shí)事,因?yàn)楸M管可以很容易地產(chǎn)生成員類型(member types),但沒有什么辦法來使用模板產(chǎn)生成員名字(member names).
一般很難去訪問一個(gè)給定類型的value 成員,即便它轉(zhuǎn)換成一個(gè)適當(dāng)?shù)幕?,這是因?yàn)槊恳粋€(gè)store 特化都派生于它的第二個(gè)參數(shù).這樣當(dāng)要訪問存儲(chǔ)在generated 中的long 的值時(shí),必須用以下方式來訪問:
也就是說訪問store 的任何成員都需要知道原始序列中在它的類型之前的所有類型.一種簡(jiǎn)化是讓編譯器的函數(shù)實(shí)參推導(dǎo)(function argument deduction)機(jī)制做推斷基類鏈(base class chain)工作:
這樣對(duì)generated 中l(wèi)ong 值的訪問可以通過下方式來進(jìn)行:
long& y=get〈long〉(generated).value;
在上面語句中:get 的第一個(gè)模板實(shí)參被限制為long,從而有效的函數(shù)參數(shù)變?yōu)閟tore〈long,U〉const&,它匹配包含一個(gè)long 成員的generated 基類.
這樣也有一個(gè)缺點(diǎn):當(dāng)member_types 中內(nèi)含重復(fù)類型時(shí),就不能使用上面所提供的get 模板函數(shù)了.
看看下面這個(gè)member_types:
mpl::vector〈short[2],long,long,char*,int〉member_types;
現(xiàn)在member_types 中有兩個(gè)long 類型.而同樣的類生成過程:
所生成的類generated 中也有兩個(gè)類型為long 的value 成員,其generated 類型為:
層次結(jié)構(gòu)見圖2所示.
圖1 generated 對(duì)象的層次結(jié)構(gòu)圖Fig.1 Hierarchical chart of generated object
圖2 generated 對(duì)象的層次結(jié)構(gòu)圖Fig.2 Hierarchical chart of generated object
如果我們?cè)賹?duì)generated 對(duì)象調(diào)用:
long& y=get〈long〉(generated).value;
C++編譯器就會(huì)抱怨出現(xiàn)模棱兩可(歧義)情況,因?yàn)間enerated 最終繼承了store〈long,U〉兩次.編譯器不能確定我們要訪問的是哪一個(gè)long 的value 值.
所以需要一個(gè)“以索引選擇member_types 中類型”的get 模板方法,而非通過型別名稱.如果可以通過藉由member_types 中指定的位置求出每個(gè)類型成員,就可以克服上述模棱兩可(歧義)的現(xiàn)象.
根據(jù)前述get 模板方法的設(shè)計(jì)思想,現(xiàn)在的想法是用int n 來代替class T.那么我們就要在編譯期通過提供的int n 來確定出class T,通過Boost 的MPL 庫提供的元函數(shù),這些設(shè)想是完全可以實(shí)現(xiàn)的.
首先通過int n 構(gòu)造出一個(gè)整型外覆器類型mpl::int_〈n〉.MPL 提供了一組外覆器模板,用于將整數(shù)值轉(zhuǎn)換成多態(tài)元數(shù)據(jù),它的模板定義為:
這里之所以要將int n 轉(zhuǎn)換成mpl::int_〈n〉,是因?yàn)楹竺娴脑瘮?shù)指定要用元數(shù)據(jù).
然后通過元函數(shù)mpl::begin〈〉救出類型序列中第一個(gè)類型元素的前向迭代器,方法為:
mpl::begin〈member_types〉::type;
接著,通過元函數(shù)mpl::advance〈〉求出n 指定位置處類型的迭代器,方法為:
最后通過mpl::deref〈〉元函數(shù)求出n 指定位置處的類型即可,完整的元函數(shù)操作為:
當(dāng)然上面全部操作也可以通過元函數(shù)mpl::at〈〉來簡(jiǎn)單實(shí)現(xiàn),即通過:
mpl::at〈member_types,mpl::int_〈n〉〉::type;
求出n 指定位置處類型.
注意上述的n 為正值,相對(duì)于mpl::vector 向量member_types 求出是前向值,但根據(jù)generated 的生成過程:
它是反序的,若要考慮反序那么全部元函數(shù)可變成:
有了上述討論,我們新的重載的get 模板方法就應(yīng)該為:
而mpl::at〈member_types,mpl::int_〈n〉〉::type 則是在程序的編譯期完成的.
那么對(duì)于mpl 的vector 序列:
mpl::vector〈short[2],long,long,char*,int〉,以下方法調(diào)用則是求出第3 個(gè)類型long 的值:
get〈3〉(generated).value;
而get〈2〉(generated).value;
則是求出第2 個(gè)類型long 的值.注意:根據(jù)函數(shù)實(shí)參推導(dǎo)(function argument deduction)機(jī)制,我們可以根據(jù)int n 推導(dǎo)出typename mpl::at〈member_types,mpl::int_〈n〉〉::type,而generated 則用來確定class U.
即使這里出現(xiàn)了兩個(gè)long 類型,但根本不會(huì)出現(xiàn)模棱兩可(歧義)的現(xiàn)象,通過本文提供的get 徹底解決的類復(fù)合成員的訪問問題.
模板元編程是C++中一種高級(jí)編程技術(shù),它處于編譯期,而類復(fù)合可以驅(qū)動(dòng)C++的編譯器在編譯期自動(dòng)生成所需的代碼,同時(shí)也將類型計(jì)算盡量提前至編譯期,減少了運(yùn)行期出錯(cuò)的機(jī)率,提高了最終程序運(yùn)行性能.本文通過以索引的方式來訪問類復(fù)合對(duì)象中的成員值,解決了歧義現(xiàn)象,解決的類復(fù)合成員中的value值的訪問問題.
[1]DAVID A.C++模板元編程[M].榮 耀,譯.北京:機(jī)械工業(yè)出版社,2010:153-155.
[2]ANDREI A.C++設(shè)計(jì)新思維[M].侯 捷,於春景,譯.武漢:華中科技大學(xué)出版社,2003:64-74.
[3]DAVID V,NICOLAI M J.C++template 中文版[M].陳偉柱,譯.北京:人民郵電出版社,2004.
[4]HERBERT S.C++完全參考手冊(cè)[M].4 版.北京:清華大學(xué)出版社,2004.
[5]王曉宇,錢紅兵.基于UML 類圖和順序圖的C++代碼自動(dòng)生成方法的研究[J].計(jì)算機(jī)應(yīng)用與軟件,2013,30(1):190-195.
[6]周 毅,顧進(jìn)廣,張曉龍,等.一種面向復(fù)合屬性的自適應(yīng)對(duì)象模型[J].計(jì)算機(jī)應(yīng)用與軟件,2008,25(11):137-139.
[7]徐靜雯,周繼恩,施躍躍,等.軟件密集型系統(tǒng)的故障診斷技術(shù)研究[J].計(jì)算機(jī)應(yīng)用與軟件,2012,29(2):175-178.
[8]黃 山,陳昱松,王建偉,等.一種基于UML 與SDL 融合建模的組件系統(tǒng)測(cè)試方法[J].計(jì)算機(jī)應(yīng)用與軟件,2011,28(7):175-177,182.
[9]唐 峰,許第洪.SolidWorks 與Pro/Engineer 之間圖形數(shù)據(jù)交換方式的研究[J].湖南師范大學(xué)自然科學(xué)學(xué)報(bào),2011,34(1):37-42.
[10]劉 震,繆 力.基于動(dòng)態(tài)調(diào)用圖的Java 程序修改影響分析技術(shù)[J].湖南師范大學(xué)自然科學(xué)學(xué)報(bào),2011,34(6):26-30.
[11]PLAUGER P J,STEPANOV A A,LEE M,et al.C++STL 中文版[M].王 昕,譯.北京:中國(guó)電力出版社,2002.
[12]BLANCHETTE J,SUMMERFIELD M.C++GUI Qt 4 編程[M].閆鋒欣,曾泉人,張志強(qiáng),譯.北京:電子工業(yè)出版社,2008.
[13]葉至軍.C++STL 開發(fā)技術(shù)導(dǎo)引[M].北京:人民郵電出版社,2007.
[14]MATTHEW H A.泛型編程與STL[M].侯 捷,譯.北京:中國(guó)電力出版社,2003.
[15]ANDREW K,BARBARA M.C++沉思錄[M].黃曉春,譯.北京:人民郵電出版社,2008.