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