一种Fortran和C++混合编程规范化方法
2014-07-18陈磊徐烂
陈磊 徐烂
摘要:目前,在科学计算领域有不少程序采用了Fortran和C++两种语言进行混合编程(即“混编”),但现有混编的程序代码可读性差,可维护性差,严重影响了软件的可移植性和可重用性。针对此现状,笔者总结了自己在核电软件开发中的实际经验,分三步阐述了Fortran和C++混合编程的规范化:首先,用宏定义等方法实现C++类型与Fortran类型的映射;然后,对于简单类型的参数传递,提出规范性意见;最后,对于Fortran中的特殊类型,提出在C++中用封装类型进行定义。经过对比验证,证明规范化的混合编程确实增加了代码的鲁棒性,可维护性。
关键词:混合编程;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++
目前,在科学计算领域有不少程序采用了Fortran和C++两种语言进行混合编程,充分利用了两者的优势,提高软件开发效率[1-2]。Fortran在过去很长一段时间作为科学计算领域的首选语言,产生了很多优秀的工程软件。随着信息技术的发展,计算机的应用性越来越广,软件的规模也随之增大,原有的Fortran结构化程序设计方法已经无法很好的胜任行业日益增长的需求。与此同时,C++这种高性能的面向对象高级语言开始深入到这一领域。利用C++语言优秀的性能和面向对象程序设计方法,计算程序不但在性能上不会与之前有所劣势,而且在程序设计和可重用性方面有显著优势。现代软件为了开发效率的提高,以及充分利用已有的Fortran软件计算包去实现目标,对C++程序语言与Fortran语言的混合编程进行了研究。现有研究主要集中于Fortran语言与C++语言实现混合编程的几种方式,以及混合编程中实现基本调用的方法总结[2-4,10],却缺乏对这方面规范性的研究,以至于现有混编程序可读性差,鲁棒性差,面向对象程序设计语言的特点利用不充分。
本文针对现在混合编程应用中存在的问题,提出如何使混合编程规范化的解决方案,使得混合编程得以优化。该文分三部分阐述了笔者的观点,并以实践中的代码片段作为论证,来说明如何优化和优化后的改变,以充分说明自己的观点。实验说明,从软件工程的角度出发,优化后的代码鲁棒性提高,便于代码维护,而且引入了面向对象的思想,真正做到了面向对象编程,在核电软件中有显著成效。
1 现有Fortran/C++混合编程
当前混编程序的方法参差不一,大致分为两类:一种是通过中间文件传递数据,一种就是直接将编译后的目标文件链接起来。前者当程序间传递数据次数少,数据量大时适用;后者则在传递数据次数多,即程序间调用频繁时适用。后者目前应用普遍,在代码实现过程中存在着代码不规范、鲁棒性差、不便于后期维护等问题,对软件生命周期具有潜在的负面影响。例如当在C++中使用LAPACK提供的软件计算包时,有些程序员会在C++头文件中以链接成功为目标简单的在头文件中添加计算函数声明,有的会直接在源文件中去声明计算函数。又如当在C++调用带字符串参数的函数时,C++需要使用char*形式的字符串传递,程序员当面对char*中的‘\0结束符时,有的选择了直接传,有的则选择使用局部临时字符数组转存后再传递。而最让程序员痛苦的是,当遇到多维数组时的下标必须减1的问题和位数必须慢慢的颠倒过来。虽然程序可以链接成功,但当程序员自己调试的时感到很吃力,错误定位慢,代码很难看,更不用说其它的程序员来维护了。endprint
为了表述的更清楚,有类似问题代码如下。
这段代码明显有上述问题,当代码规模增大,程序将变得难以维护。这样的代码即使勉强运行,而代码的鲁棒性、可维护性无从谈之。
2 FORTRAN/C++混合编程规范化
首先,在C++中正确声明FORTRAN接口,需要从链接约定、调用约定和基本数据类型映射着手。
2.1 链接约定
在C++中,几个函数可以有相同的函数名,主要参数的类型或个数不一致就行。这个特征就是函数重载。函数重载是用了“name mangling”的技术,这个是由编译器内部实现的。但是FORTRAN中没有函数重载的概念。为了使C++编译器能够认识到FORTRAN编译器生成的代码,“name mangling”的功能必须关闭。因此当在C++中定义SUBROUTINE和FUNCTION时候需要包含extern “C”, 如下表1所示。
表1 对应函数声明
[FORTRAN函数定义\&C++函数定义\&SUBROUTINE FSUB()\&extern “C” void FSUB()\&REAL FUNCTIONF ()\&extern “C” float FFUNC()\&]
2.2 调用约定
C++和Fortran在调用约定上各不相同。参数是如何放入调用堆栈的,栈是由调用程序还是被调用程序负责清理的,当然这是和平台息息相关的。通常,C++遵循的是__cdecl约定,而FORTRAN遵循的是__stdcall约定,并且这两个一般都是不兼容的。因此需要明确指定C++调用约定以确保两者一致,这个可以通过编译器提供的选项进行设置。
2.3 基本数据类型映射
两者的基本数据类型对比表,如表2所示。
表2 数据类型映射
[FORTRAN数据类型\&C++数据类型\&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函数的原型,需要经过几步处理。为了提高程序的可读性,代码的可重用性,以及减少代码重复率,因此笔者添加了一个头文件“FORTRAN.h”,对其共性加以提取,定义了一系列宏和重命名类型,使得程序接口声明简洁,减少代码冗余。“FORTRAN.h”关键部分定义如下所示。
[INTEGER_FUNCTION F77FUNC();
SUBROUTINE F77SUB();]
那么当在C++中声明一个FORTRAN中的SUBROUTINE和FUNCTION时,只要简单声明如下。
这种解决方案为C++和Fortran混合编程中接口的定义提供了一定的通用性,增强了可读性,对接口做了最基本的优化,并为进一步优化奠定了良好的基础。
其次,有了前面初步成果之后,面对几种形式的形参,充分应用C++的特性提出良好的解决方案,使代码具有鲁棒性,可维护性。
2.4 非数组参数
非数组参数优化,指的是对接口中不含数组参数的函数进行接口优化处理。当在C++中声明一个带有基本类型参数的FORTRAN函数原型时,需要了解FORTRAN参数传递过程的实质。因为FORTRAN函数参数在传递时相当于C++中的按引用传参的方式,因此在C++中声明时,需要在对应的类型名后面加上符号&。
当然面对类似函数,有的程序员选择将函数形参声明为指针类型而不是引用类型,笔者认为声明为引用类型更好,更安全更高效。
2.5 简单数组参数
这里讨论的主要是函数参数为一维数组时,如何传递参数。这里抓住数组实现的基本原理——数组传递时是传递数组第一个元素的首地址。因此在声明函数原型时只要将参数声明为数组元素类型的指针即可。
最后,针对多维数组和字符串这两种特殊参数进行探讨。
2.6 多维数组参数
FORTRAN和C++中的数组在存取方式上是截然不同的。FORTRAN中数组元素是按列优先存取的,而C++中是按行优先存取的。除此之外,FORTRAN中的数组起始下标默认是1,而C++中起始下标是0。显然当要从C++中传递一个多维数组参数时,数组元素需要转置并且减1。如表3所示。
表3 数组形式对比
[FORTRAN数组元素\&C++中数组元素\&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
}; ]
实践证明这种方式的参数传递是不合适的,严重影响了原有的C++程序的可读性、可维护性。因此为了摆脱“为了重用FORTRAN而FORTRAN”的方式,充分利用C++面向对象的特性,可以建立一个类似于FORTRAN的ARRAY模板类(这里只讨论二维数组)。定义如下所示。
此模板类构造函数实例化由三个普通参数(包括C++数组首地址(可选)、维度和列数)和一个模板类型参数决定。此类中包含两个成员函数和四个数据成员,成员函数operator T*()实现的操作是返回数据成员f77rep, 而另一个成员函数实现了FORTRAN种ARRAY的索引操作。
对于这种基于类的解决方案,很好的解决了C++与FORTRAN之间多维数组的差异,同时使程序现代化,对象化。程序也将更易于维护,可读性更好。
2.7 字符串传递参数
字符串传参相对麻烦一点,简单用typedef不能有效的与FORTRAN中的CHARACTER类型兼容。原因主要有两个:首先,C++中的用char*类型代表一个字符串,每个字符串以\0结束,同时也依赖于这个结束符取得字符串长度。而在FORTRAN中,字符串不是以\0结束的,而是通常将这个字符串的长度存储在一个INTEGER类型的变量中,只能通过FORTRAN提供的内部函数LEN(string)获取长度;其次,FORTRAN标准并没有明确指明如何在SUBROUTINES和FUNCTIONS之间传递参数。因此,不同的几个FORTRAN编译器之间的实现有些不兼容。如果用C++的术语描述那些不同实现的话,一些编译器通过传递一种自定义struct,struct中包含了一个char*类型成员代表字符串值和一个size_t类型成员代表字符串长度;另一些是直接传递一个char*类型字符串,然后将字符串长度隐藏在参数列表后传递过去。用其中任意一种方法,都将使代码失去可移植性,并且代码可读性差,很容易就出错。
[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语言之间的这种差异,使字符串传递简化且仍然维持着可移植性,我们就应该充分发挥C++中类的特性,通过自定义一个CHARACTER类来作为一个良好的解决方案。类定义如下 。
此自定义类型在C++语言中等同于Fortran的CHARACTER类型。此类型中定义的几个方法与Fortran中CHARACTER类型相同。同时,它需要有一个具体存取字符串和一个存取字符串长度的size_t的数据成员。
这样的一种新类型的定义能更好的实现C++与FORTRAN之间的字符串传递,对混合编程组成程序的可移植性也是有很大改善的。
3 实验结果对比
下面就通过使用提出的方法来重新编写与问题代码功能类似的程序,代码如下。
[//函数头文件 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;
}\&]
结果表明,需要引用函数只需要在头文件中简单的声明,公共的头文件统一调控调用约定、链接约定、类型映射等,使得程序鲁棒性,可维护性得到提高。在实际调用时,操作也相对更加便捷,如若FORTRAN语言因编译器有所变化表现略有不同,也只要维护一个类文件即可。从软件工程的角度,这种规范化的混合编程改善了软件的健壮性,可维护性,值得在混合编程中提倡。
4 结束语
混合编程使得语言之间扬长避短、互相促进,将已有优质资源的效用最大化。该文提出的规范化方法保证我们混合编程后的程序仍然是有高可重用性和可移植性的,在核电软件中有显著成效,使软件真正向工程化、现代化迈进。
参考文献:
[1] 许庆国,金思毅,周传光.Fortran 源程序在 WindowsOOP 环境下的应用[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].现代计算机(专业版),2012(19).
[7] 王家华,周润.MATLAB与Visual C++混合编程在储层三维建模系统中的应用[J].软件导刊,2011(1).
[8] 侍孝虎.VB与MATLAB混合编程研究与实现[J].软件导刊,2012(9).
[9] 李霞,亓雪冬.基于Linux的Fortran与C/C++混合编程[J].现代计算机(专业版), 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;
}\&]
结果表明,需要引用函数只需要在头文件中简单的声明,公共的头文件统一调控调用约定、链接约定、类型映射等,使得程序鲁棒性,可维护性得到提高。在实际调用时,操作也相对更加便捷,如若FORTRAN语言因编译器有所变化表现略有不同,也只要维护一个类文件即可。从软件工程的角度,这种规范化的混合编程改善了软件的健壮性,可维护性,值得在混合编程中提倡。
4 结束语
混合编程使得语言之间扬长避短、互相促进,将已有优质资源的效用最大化。该文提出的规范化方法保证我们混合编程后的程序仍然是有高可重用性和可移植性的,在核电软件中有显著成效,使软件真正向工程化、现代化迈进。
参考文献:
[1] 许庆国,金思毅,周传光.Fortran 源程序在 WindowsOOP 环境下的应用[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].现代计算机(专业版),2012(19).
[7] 王家华,周润.MATLAB与Visual C++混合编程在储层三维建模系统中的应用[J].软件导刊,2011(1).
[8] 侍孝虎.VB与MATLAB混合编程研究与实现[J].软件导刊,2012(9).
[9] 李霞,亓雪冬.基于Linux的Fortran与C/C++混合编程[J].现代计算机(专业版), 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;
}\&]
结果表明,需要引用函数只需要在头文件中简单的声明,公共的头文件统一调控调用约定、链接约定、类型映射等,使得程序鲁棒性,可维护性得到提高。在实际调用时,操作也相对更加便捷,如若FORTRAN语言因编译器有所变化表现略有不同,也只要维护一个类文件即可。从软件工程的角度,这种规范化的混合编程改善了软件的健壮性,可维护性,值得在混合编程中提倡。
4 结束语
混合编程使得语言之间扬长避短、互相促进,将已有优质资源的效用最大化。该文提出的规范化方法保证我们混合编程后的程序仍然是有高可重用性和可移植性的,在核电软件中有显著成效,使软件真正向工程化、现代化迈进。
参考文献:
[1] 许庆国,金思毅,周传光.Fortran 源程序在 WindowsOOP 环境下的应用[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].现代计算机(专业版),2012(19).
[7] 王家华,周润.MATLAB与Visual C++混合编程在储层三维建模系统中的应用[J].软件导刊,2011(1).
[8] 侍孝虎.VB与MATLAB混合编程研究与实现[J].软件导刊,2012(9).
[9] 李霞,亓雪冬.基于Linux的Fortran与C/C++混合编程[J].现代计算机(专业版), 2012(5).
[10] 李慧,韩一平,华彩成,李存志.Visual C++与Fortran混合编程在电磁散射中的应用[J]. 微波学报,2012(6).