陳磊 徐爛
摘要:目前,在科學計算領(lǐng)域有不少程序采用了Fortran和C++兩種語言進行混合編程(即“混編”),但現(xiàn)有混編的程序代碼可讀性差,可維護性差,嚴重影響了軟件的可移植性和可重用性。針對此現(xiàn)狀,筆者總結(jié)了自己在核電軟件開發(fā)中的實際經(jīng)驗,分三步闡述了Fortran和C++混合編程的規(guī)范化:首先,用宏定義等方法實現(xiàn)C++類型與Fortran類型的映射;然后,對于簡單類型的參數(shù)傳遞,提出規(guī)范性意見;最后,對于Fortran中的特殊類型,提出在C++中用封裝類型進行定義。經(jīng)過對比驗證,證明規(guī)范化的混合編程確實增加了代碼的魯棒性,可維護性。
關(guān)鍵詞:混合編程;FORTRAN;C++
中圖分類號:TP311 文獻標識碼:A 文章編號:1009-3044(2014)13-2962-05
A Normalization Method of Mixed-programming with FORTRAN and C++
CHEN Lei, XU Lan
(College of Computer Science and Technology, University of South China, Hengyang 421001, China)
Abstract: Nowadays in scientific computing, a mixed-programming technology with FORTRAN and C++ program language is adopted in many programs. While lots of mixed programs have the problems of bad readability and maintainability, so it have a bad influence on the reusability of software. According to the situation, author sum up experiences and come up with a solution how to improve the mix-programming by three step. Firstly, use a macro definition method to realize the type-map between FORTRAN and C++. Secondly, to the base-type parameter passing, propose an idea about normalization. At last, propose a solution that use a C++ use-defined type for the special type in FORTRAN. By validating the results of before the normalization and after it, mixed-programming with normalization did improve the maintainability and robustness of the code.
Key words: mixed-programming; FORTRAN; C++
目前,在科學計算領(lǐng)域有不少程序采用了Fortran和C++兩種語言進行混合編程,充分利用了兩者的優(yōu)勢,提高軟件開發(fā)效率[1-2]。Fortran在過去很長一段時間作為科學計算領(lǐng)域的首選語言,產(chǎn)生了很多優(yōu)秀的工程軟件。隨著信息技術(shù)的發(fā)展,計算機的應用性越來越廣,軟件的規(guī)模也隨之增大,原有的Fortran結(jié)構(gòu)化程序設計方法已經(jīng)無法很好的勝任行業(yè)日益增長的需求。與此同時,C++這種高性能的面向?qū)ο蟾呒壵Z言開始深入到這一領(lǐng)域。利用C++語言優(yōu)秀的性能和面向?qū)ο蟪绦蛟O計方法,計算程序不但在性能上不會與之前有所劣勢,而且在程序設計和可重用性方面有顯著優(yōu)勢?,F(xiàn)代軟件為了開發(fā)效率的提高,以及充分利用已有的Fortran軟件計算包去實現(xiàn)目標,對C++程序語言與Fortran語言的混合編程進行了研究?,F(xiàn)有研究主要集中于Fortran語言與C++語言實現(xiàn)混合編程的幾種方式,以及混合編程中實現(xiàn)基本調(diào)用的方法總結(jié)[2-4,10],卻缺乏對這方面規(guī)范性的研究,以至于現(xiàn)有混編程序可讀性差,魯棒性差,面向?qū)ο蟪绦蛟O計語言的特點利用不充分。
本文針對現(xiàn)在混合編程應用中存在的問題,提出如何使混合編程規(guī)范化的解決方案,使得混合編程得以優(yōu)化。該文分三部分闡述了筆者的觀點,并以實踐中的代碼片段作為論證,來說明如何優(yōu)化和優(yōu)化后的改變,以充分說明自己的觀點。實驗說明,從軟件工程的角度出發(fā),優(yōu)化后的代碼魯棒性提高,便于代碼維護,而且引入了面向?qū)ο蟮乃枷?,真正做到了面向?qū)ο缶幊蹋诤穗娷浖杏酗@著成效。
1 現(xiàn)有Fortran/C++混合編程
當前混編程序的方法參差不一,大致分為兩類:一種是通過中間文件傳遞數(shù)據(jù),一種就是直接將編譯后的目標文件鏈接起來。前者當程序間傳遞數(shù)據(jù)次數(shù)少,數(shù)據(jù)量大時適用;后者則在傳遞數(shù)據(jù)次數(shù)多,即程序間調(diào)用頻繁時適用。后者目前應用普遍,在代碼實現(xiàn)過程中存在著代碼不規(guī)范、魯棒性差、不便于后期維護等問題,對軟件生命周期具有潛在的負面影響。例如當在C++中使用LAPACK提供的軟件計算包時,有些程序員會在C++頭文件中以鏈接成功為目標簡單的在頭文件中添加計算函數(shù)聲明,有的會直接在源文件中去聲明計算函數(shù)。又如當在C++調(diào)用帶字符串參數(shù)的函數(shù)時,C++需要使用char*形式的字符串傳遞,程序員當面對char*中的‘\0結(jié)束符時,有的選擇了直接傳,有的則選擇使用局部臨時字符數(shù)組轉(zhuǎn)存后再傳遞。而最讓程序員痛苦的是,當遇到多維數(shù)組時的下標必須減1的問題和位數(shù)必須慢慢的顛倒過來。雖然程序可以鏈接成功,但當程序員自己調(diào)試的時感到很吃力,錯誤定位慢,代碼很難看,更不用說其它的程序員來維護了。endprint
為了表述的更清楚,有類似問題代碼如下。
這段代碼明顯有上述問題,當代碼規(guī)模增大,程序?qū)⒆兊秒y以維護。這樣的代碼即使勉強運行,而代碼的魯棒性、可維護性無從談之。
2 FORTRAN/C++混合編程規(guī)范化
首先,在C++中正確聲明FORTRAN接口,需要從鏈接約定、調(diào)用約定和基本數(shù)據(jù)類型映射著手。
2.1 鏈接約定
在C++中,幾個函數(shù)可以有相同的函數(shù)名,主要參數(shù)的類型或個數(shù)不一致就行。這個特征就是函數(shù)重載。函數(shù)重載是用了“name mangling”的技術(shù),這個是由編譯器內(nèi)部實現(xiàn)的。但是FORTRAN中沒有函數(shù)重載的概念。為了使C++編譯器能夠認識到FORTRAN編譯器生成的代碼,“name mangling”的功能必須關(guān)閉。因此當在C++中定義SUBROUTINE和FUNCTION時候需要包含extern “C”, 如下表1所示。
表1 對應函數(shù)聲明
[FORTRAN函數(shù)定義\&C++函數(shù)定義\&SUBROUTINE FSUB()\&extern “C” void FSUB()\&REAL FUNCTIONF ()\&extern “C” float FFUNC()\&]
2.2 調(diào)用約定
C++和Fortran在調(diào)用約定上各不相同。參數(shù)是如何放入調(diào)用堆棧的,棧是由調(diào)用程序還是被調(diào)用程序負責清理的,當然這是和平臺息息相關(guān)的。通常,C++遵循的是__cdecl約定,而FORTRAN遵循的是__stdcall約定,并且這兩個一般都是不兼容的。因此需要明確指定C++調(diào)用約定以確保兩者一致,這個可以通過編譯器提供的選項進行設置。
2.3 基本數(shù)據(jù)類型映射
兩者的基本數(shù)據(jù)類型對比表,如表2所示。
表2 數(shù)據(jù)類型映射
[FORTRAN數(shù)據(jù)類型\&C++數(shù)據(jù)類型\&INTEGER\&int\&REAL\&float\&LOGICAL\&unsigned int\&DOUBLE PRECISION\&double\&]
[#ifdef UNIX
#define SUBROUTINE extern “C” void
#define INTEGER_FUNCTION extern “C” INTEGER
#define REAL_FUNCTION extern “C” REAL
#define LOGICAL_FUNCTION extern “C” LOGICAL
#define DOUBLE_PRECISION_FUNCTION extern “C” DOUBLE_PRECISION
#else
#define SUBROUTINE extern “C” void __stdcall
#define INTEGER_FUNCTION extern “C” INTEGER __stdcall
#define REAL_FUNCTION extern “C” REAL __stdcall
#define LOGICAL_FUNCTION extern “C” LOGICAL __stdcall
#define DOUBLE_PRECISION_FUNCTION extern “C” DOUBLE_PRECISION __stdcall
#endif]
如上所述,要想在C++中定義好Fortran函數(shù)的原型,需要經(jīng)過幾步處理。為了提高程序的可讀性,代碼的可重用性,以及減少代碼重復率,因此筆者添加了一個頭文件“FORTRAN.h”,對其共性加以提取,定義了一系列宏和重命名類型,使得程序接口聲明簡潔,減少代碼冗余?!癋ORTRAN.h”關(guān)鍵部分定義如下所示。
[INTEGER_FUNCTION F77FUNC();
SUBROUTINE F77SUB();]
那么當在C++中聲明一個FORTRAN中的SUBROUTINE和FUNCTION時,只要簡單聲明如下。
這種解決方案為C++和Fortran混合編程中接口的定義提供了一定的通用性,增強了可讀性,對接口做了最基本的優(yōu)化,并為進一步優(yōu)化奠定了良好的基礎。
其次,有了前面初步成果之后,面對幾種形式的形參,充分應用C++的特性提出良好的解決方案,使代碼具有魯棒性,可維護性。
2.4 非數(shù)組參數(shù)
非數(shù)組參數(shù)優(yōu)化,指的是對接口中不含數(shù)組參數(shù)的函數(shù)進行接口優(yōu)化處理。當在C++中聲明一個帶有基本類型參數(shù)的FORTRAN函數(shù)原型時,需要了解FORTRAN參數(shù)傳遞過程的實質(zhì)。因為FORTRAN函數(shù)參數(shù)在傳遞時相當于C++中的按引用傳參的方式,因此在C++中聲明時,需要在對應的類型名后面加上符號&。
當然面對類似函數(shù),有的程序員選擇將函數(shù)形參聲明為指針類型而不是引用類型,筆者認為聲明為引用類型更好,更安全更高效。
2.5 簡單數(shù)組參數(shù)
這里討論的主要是函數(shù)參數(shù)為一維數(shù)組時,如何傳遞參數(shù)。這里抓住數(shù)組實現(xiàn)的基本原理——數(shù)組傳遞時是傳遞數(shù)組第一個元素的首地址。因此在聲明函數(shù)原型時只要將參數(shù)聲明為數(shù)組元素類型的指針即可。
最后,針對多維數(shù)組和字符串這兩種特殊參數(shù)進行探討。
2.6 多維數(shù)組參數(shù)
FORTRAN和C++中的數(shù)組在存取方式上是截然不同的。FORTRAN中數(shù)組元素是按列優(yōu)先存取的,而C++中是按行優(yōu)先存取的。除此之外,F(xiàn)ORTRAN中的數(shù)組起始下標默認是1,而C++中起始下標是0。顯然當要從C++中傳遞一個多維數(shù)組參數(shù)時,數(shù)組元素需要轉(zhuǎn)置并且減1。如表3所示。
表3 數(shù)組形式對比
[FORTRAN數(shù)組元素\&C++中數(shù)組元素\&ARRAY(I,J,K)\&ARRAY[K-1][J-1][I-1]\&]
[template
class FMATRIX
{
public:
FMATRIX(size_t dim1, size_t dim2);]
[FMATRIX(T* cpparr, size_t dim1, size_t dim2);
operator T*();
T& operator()(size_t index1, size_t index2);
~FMATRIX();
public:
const size_t ndim; // number of array dimensions
size_t dim[7]; // size of each dimension
T* cpprep; // original c++ array
T* f77rep; // array used by FORTRAN
}; ]
實踐證明這種方式的參數(shù)傳遞是不合適的,嚴重影響了原有的C++程序的可讀性、可維護性。因此為了擺脫“為了重用FORTRAN而FORTRAN”的方式,充分利用C++面向?qū)ο蟮奶匦?,可以建立一個類似于FORTRAN的ARRAY模板類(這里只討論二維數(shù)組)。定義如下所示。
此模板類構(gòu)造函數(shù)實例化由三個普通參數(shù)(包括C++數(shù)組首地址(可選)、維度和列數(shù))和一個模板類型參數(shù)決定。此類中包含兩個成員函數(shù)和四個數(shù)據(jù)成員,成員函數(shù)operator T*()實現(xiàn)的操作是返回數(shù)據(jù)成員f77rep, 而另一個成員函數(shù)實現(xiàn)了FORTRAN種ARRAY的索引操作。
對于這種基于類的解決方案,很好的解決了C++與FORTRAN之間多維數(shù)組的差異,同時使程序現(xiàn)代化,對象化。程序也將更易于維護,可讀性更好。
2.7 字符串傳遞參數(shù)
字符串傳參相對麻煩一點,簡單用typedef不能有效的與FORTRAN中的CHARACTER類型兼容。原因主要有兩個:首先,C++中的用char*類型代表一個字符串,每個字符串以\0結(jié)束,同時也依賴于這個結(jié)束符取得字符串長度。而在FORTRAN中,字符串不是以\0結(jié)束的,而是通常將這個字符串的長度存儲在一個INTEGER類型的變量中,只能通過FORTRAN提供的內(nèi)部函數(shù)LEN(string)獲取長度;其次,F(xiàn)ORTRAN標準并沒有明確指明如何在SUBROUTINES和FUNCTIONS之間傳遞參數(shù)。因此,不同的幾個FORTRAN編譯器之間的實現(xiàn)有些不兼容。如果用C++的術(shù)語描述那些不同實現(xiàn)的話,一些編譯器通過傳遞一種自定義struct,struct中包含了一個char*類型成員代表字符串值和一個size_t類型成員代表字符串長度;另一些是直接傳遞一個char*類型字符串,然后將字符串長度隱藏在參數(shù)列表后傳遞過去。用其中任意一種方法,都將使代碼失去可移植性,并且代碼可讀性差,很容易就出錯。
[class CHARACTER
{
public:
CHARACTER(char* cstring);
CHARACTER(char* cstring, const size_t lstr);
~CHARACTER();
CHARACTER operator()(size_t index);
void pad(size_t first,size_t howmany=1);
void operator=(char* str);
operator char*();
public:
char* rep; // Actual string
size_t len; // String length
};
]
為了處理C++和FORTRAN語言之間的這種差異,使字符串傳遞簡化且仍然維持著可移植性,我們就應該充分發(fā)揮C++中類的特性,通過自定義一個CHARACTER類來作為一個良好的解決方案。類定義如下 。
此自定義類型在C++語言中等同于Fortran的CHARACTER類型。此類型中定義的幾個方法與Fortran中CHARACTER類型相同。同時,它需要有一個具體存取字符串和一個存取字符串長度的size_t的數(shù)據(jù)成員。
這樣的一種新類型的定義能更好的實現(xiàn)C++與FORTRAN之間的字符串傳遞,對混合編程組成程序的可移植性也是有很大改善的。
3 實驗結(jié)果對比
下面就通過使用提出的方法來重新編寫與問題代碼功能類似的程序,代碼如下。
[//函數(shù)頭文件 func.h
#include “FORTRAN.h”
INTEGER_FUNCTION one(INTEGER&,REAL&,DOUBLE_PRECITION&);
…
INTEGER_FUCNTION three(INTEGER*);
INTEGER_FUCNTION four(CHARACTER);
//源代碼main.cpp文件
#include “func.h”
int main()
{ …
one(m,n,a);
INTEGER arr[5]={2,3,4,5,6};
two(arr);
. FARRAY
three(arr2);
//要取計算后第三對值arr2(3,1)和arr2(3,2),這樣能很好的知道原來程序的意圖
CHARACTER STR(”hello world!”);
four(STR);
return 0;
}\&]
結(jié)果表明,需要引用函數(shù)只需要在頭文件中簡單的聲明,公共的頭文件統(tǒng)一調(diào)控調(diào)用約定、鏈接約定、類型映射等,使得程序魯棒性,可維護性得到提高。在實際調(diào)用時,操作也相對更加便捷,如若FORTRAN語言因編譯器有所變化表現(xiàn)略有不同,也只要維護一個類文件即可。從軟件工程的角度,這種規(guī)范化的混合編程改善了軟件的健壯性,可維護性,值得在混合編程中提倡。
4 結(jié)束語
混合編程使得語言之間揚長避短、互相促進,將已有優(yōu)質(zhì)資源的效用最大化。該文提出的規(guī)范化方法保證我們混合編程后的程序仍然是有高可重用性和可移植性的,在核電軟件中有顯著成效,使軟件真正向工程化、現(xiàn)代化邁進。
參考文獻:
[1] 許慶國,金思毅,周傳光.Fortran 源程序在 WindowsOOP 環(huán)境下的應用[J].計算機工程, 2001,27(9):169-171.
[2] 周振紅,顏國紅,吳虹娟.Fortran 與 Visual C++ 混合編程研究[J].武漢大學學報:工學版,2001,34(2):84-87.
[3] Stanley B, Lippman.Essentialc++[M].武漢:華中科技大學出版社,2001
[4] Burkhard Burow. Mixed language programming[C]. Rio de Ja-neiro, Brazil: Computing in High Energy Physics, 1999.
[5] Theurich G, Anson B, Hill N A. Making the Fortran-to-C transi-tion: how painful is it really[J]. Computing in Science & Engi-neering, 2001,3(1):21-27.
[6] 汪莉,張麗華,張國慶.Visual C++工控軟件中自定義圖表控件編程設計與應用[J].現(xiàn)代計算機(專業(yè)版),2012(19).
[7] 王家華,周潤.MATLAB與Visual C++混合編程在儲層三維建模系統(tǒng)中的應用[J].軟件導刊,2011(1).
[8] 侍孝虎.VB與MATLAB混合編程研究與實現(xiàn)[J].軟件導刊,2012(9).
[9] 李霞,亓雪冬.基于Linux的Fortran與C/C++混合編程[J].現(xiàn)代計算機(專業(yè)版), 2012(5).
[10] 李慧,韓一平,華彩成,李存志.Visual C++與Fortran混合編程在電磁散射中的應用[J]. 微波學報,2012(6).
one(m,n,a);
INTEGER arr[5]={2,3,4,5,6};
two(arr);
. FARRAY
three(arr2);
//要取計算后第三對值arr2(3,1)和arr2(3,2),這樣能很好的知道原來程序的意圖
CHARACTER STR(”hello world!”);
four(STR);
return 0;
}\&]
結(jié)果表明,需要引用函數(shù)只需要在頭文件中簡單的聲明,公共的頭文件統(tǒng)一調(diào)控調(diào)用約定、鏈接約定、類型映射等,使得程序魯棒性,可維護性得到提高。在實際調(diào)用時,操作也相對更加便捷,如若FORTRAN語言因編譯器有所變化表現(xiàn)略有不同,也只要維護一個類文件即可。從軟件工程的角度,這種規(guī)范化的混合編程改善了軟件的健壯性,可維護性,值得在混合編程中提倡。
4 結(jié)束語
混合編程使得語言之間揚長避短、互相促進,將已有優(yōu)質(zhì)資源的效用最大化。該文提出的規(guī)范化方法保證我們混合編程后的程序仍然是有高可重用性和可移植性的,在核電軟件中有顯著成效,使軟件真正向工程化、現(xiàn)代化邁進。
參考文獻:
[1] 許慶國,金思毅,周傳光.Fortran 源程序在 WindowsOOP 環(huán)境下的應用[J].計算機工程, 2001,27(9):169-171.
[2] 周振紅,顏國紅,吳虹娟.Fortran 與 Visual C++ 混合編程研究[J].武漢大學學報:工學版,2001,34(2):84-87.
[3] Stanley B, Lippman.Essentialc++[M].武漢:華中科技大學出版社,2001
[4] Burkhard Burow. Mixed language programming[C]. Rio de Ja-neiro, Brazil: Computing in High Energy Physics, 1999.
[5] Theurich G, Anson B, Hill N A. Making the Fortran-to-C transi-tion: how painful is it really[J]. Computing in Science & Engi-neering, 2001,3(1):21-27.
[6] 汪莉,張麗華,張國慶.Visual C++工控軟件中自定義圖表控件編程設計與應用[J].現(xiàn)代計算機(專業(yè)版),2012(19).
[7] 王家華,周潤.MATLAB與Visual C++混合編程在儲層三維建模系統(tǒng)中的應用[J].軟件導刊,2011(1).
[8] 侍孝虎.VB與MATLAB混合編程研究與實現(xiàn)[J].軟件導刊,2012(9).
[9] 李霞,亓雪冬.基于Linux的Fortran與C/C++混合編程[J].現(xiàn)代計算機(專業(yè)版), 2012(5).
[10] 李慧,韓一平,華彩成,李存志.Visual C++與Fortran混合編程在電磁散射中的應用[J]. 微波學報,2012(6).
one(m,n,a);
INTEGER arr[5]={2,3,4,5,6};
two(arr);
. FARRAY
three(arr2);
//要取計算后第三對值arr2(3,1)和arr2(3,2),這樣能很好的知道原來程序的意圖
CHARACTER STR(”hello world!”);
four(STR);
return 0;
}\&]
結(jié)果表明,需要引用函數(shù)只需要在頭文件中簡單的聲明,公共的頭文件統(tǒng)一調(diào)控調(diào)用約定、鏈接約定、類型映射等,使得程序魯棒性,可維護性得到提高。在實際調(diào)用時,操作也相對更加便捷,如若FORTRAN語言因編譯器有所變化表現(xiàn)略有不同,也只要維護一個類文件即可。從軟件工程的角度,這種規(guī)范化的混合編程改善了軟件的健壯性,可維護性,值得在混合編程中提倡。
4 結(jié)束語
混合編程使得語言之間揚長避短、互相促進,將已有優(yōu)質(zhì)資源的效用最大化。該文提出的規(guī)范化方法保證我們混合編程后的程序仍然是有高可重用性和可移植性的,在核電軟件中有顯著成效,使軟件真正向工程化、現(xiàn)代化邁進。
參考文獻:
[1] 許慶國,金思毅,周傳光.Fortran 源程序在 WindowsOOP 環(huán)境下的應用[J].計算機工程, 2001,27(9):169-171.
[2] 周振紅,顏國紅,吳虹娟.Fortran 與 Visual C++ 混合編程研究[J].武漢大學學報:工學版,2001,34(2):84-87.
[3] Stanley B, Lippman.Essentialc++[M].武漢:華中科技大學出版社,2001
[4] Burkhard Burow. Mixed language programming[C]. Rio de Ja-neiro, Brazil: Computing in High Energy Physics, 1999.
[5] Theurich G, Anson B, Hill N A. Making the Fortran-to-C transi-tion: how painful is it really[J]. Computing in Science & Engi-neering, 2001,3(1):21-27.
[6] 汪莉,張麗華,張國慶.Visual C++工控軟件中自定義圖表控件編程設計與應用[J].現(xiàn)代計算機(專業(yè)版),2012(19).
[7] 王家華,周潤.MATLAB與Visual C++混合編程在儲層三維建模系統(tǒng)中的應用[J].軟件導刊,2011(1).
[8] 侍孝虎.VB與MATLAB混合編程研究與實現(xiàn)[J].軟件導刊,2012(9).
[9] 李霞,亓雪冬.基于Linux的Fortran與C/C++混合編程[J].現(xiàn)代計算機(專業(yè)版), 2012(5).
[10] 李慧,韓一平,華彩成,李存志.Visual C++與Fortran混合編程在電磁散射中的應用[J]. 微波學報,2012(6).