正文

Delphi 与 FORTRAN语言的混合编程 2006-04-04 14:36:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/atom/11925.html

分享到:

 

转载按:这里 仅介绍一种思路,就是混合语言编程的思路,虽然本人并未学习Delphi,但是希望这种过程和思路能对程序开发有一定的帮助!!

众所周知,FORTRAN强于数值计算,尤其是如果计算主要针对复数进行,则FORTRAN更有无可比拟的优势。FORTRAN是所有语言中唯一将复数定义为一种标准数据类型的语言。但是FORTRAN语言在可视化程序设计方面是非常欠缺的,至少目前还没有一家厂商推出具有RAD特性的FORTRAN编译集成开发环境。因此,当用FORTRAN实现了一种大型的科学计算以后,却难以将这种计算转变为数据输入简易、结果显示方便的WINDOWS可视化应用程序。这一点,采用Delphi很容易实现。因此,在许多情况下,使用FORTRANDelphi的混合编程可同时具有二者的优点。

本文采用两种不同的方法来实现混合编程。一种是直接执行可执行文件的方式,一种是调用动态链接库中子程序的方式。在第一种方式下,在Delphi程序设计中直接执行FORTRAN程序的执行文件,通过文件来进行数据交换;在第二种情况下,首先将FORTRAN程序编译成动态链接库(DLL),在Delphi程序设计中,调用此动态链接库中某个子程序来完成某项计算。这两种方式各有优缺点。第一种方式的调试较为简单,不存在不同语言之间的数据类型的不匹配问题。但是,这种方式下,在Delphi中无法实现对程序运行的有效监督,同时,以文件进行数据交换在操作中也不太方便,效率也不高。第二种方式则整合了两种程序之间的差别,如果编制成功,程序运行时看不出混合语言编程的痕迹,但是这种方式调试起来特别麻烦。一般说来,对于已有的输入输出较为复杂的FORTRAN程序,可以考虑第一种方式,而对于相对简单的,或者自己着手编制的新的程序,可选用第二种。

1.执行可执行文件(exe)方式

Windows中提供了API函数WinExec来执行存在的执行文件。该函数定义为:

UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow );

参数说明如下:
        LPCSTR lpCmdLine: 包含要执行的命令行。

系统将在以下范围查找应用程序:
            应用程序启动位置
            当前目录位置
            Windows system目录
            Windows 目录
            path中设置的路径列表
        UINT uCmdShow: 定义了以怎样的形式启动程序的常数值。具体说明如下:
           SW_HIDE 隐藏窗口,活动状态给令一个窗口 
           SW_MINIMIZE 最小化窗口,活动状态给令一个窗口
 
           SW_RESTORE 用原来的大小和位置显示一个窗口,同时令其进入活动状态
 
           SW_SHOW 用当前的大小和位置显示一个窗口,同时令其进入活动状态
 
           SW_SHOWMAXIMIZED 最大化窗口,并将其激活
 
           SW_SHOWMINIMIZED 最小化窗口,并将其激活
 
           SW_SHOWMINNOACTIVE 最小化一个窗口,同时不改变活动窗口
 
           SW_SHOWNA 用当前的大小和位置显示一个窗口,不改变活动窗口
 
           SW_SHOWNOACTIVATE 用最近的大小和位置显示一个窗口,同时不改变活动窗口
 
           SW_SHOWNORMAL SW_RESTORE相同 

如果Str为一记录可执行文件的路径及文件名变量,则WinExec ( Pchar ( Str ), SW_SHOWNORMAL )表示在正常状况下执行该可执行文件。

2.调用动态链接库(DLL)方式

第二种方法比第一种方法实现起来麻烦一些。在这种方法中,FORTRAN程序首先被编译成Windows标准的动态链接库文件(DLL, Dynamic-Link Library),然后在Delphi中调用。在FORTRAN语言程序设计中,本文采用Compaq Visual Fortran6.6编译器,可以容易地生成动态链接库。

在这种方式混合编程中,由于需要在两种不同的语言之间进行内存中的数据交换,因此,其数据类型必须一一对应。由于不同语言的数据类型所对应的存储方式、数据传递方式不尽相同,而且程序调试需要在两个不同的编译器中进行,因此这种方法编译调试较为麻烦,不易解决编译中出现的一些问题。

在生成动态链接库的FORTRAN子程序中,必须采用以下方法进行说明:

DECATTRIBUTES DLLEXPORT::SUB_NAME
      DECATTRIBUTES ALIAS:’Sub_AliasName’::SUB_NAME

上面第一句话中,关键字DLLEXPORT表明这个子程序在动态链接库中可被外部调用,SUB_NAME为此子程序在动态链接库中的程序名;第二句话中的ALIAS给该程序名另赋一个别名,因为FORTRAN默认情况下编译出的程序名为大写字母,别名中可以改变。

函数和程序的调用中,参数传递的方式有两种。一种是传递参数地址的方式,即Call by Refence,另一种是传递值的方式,即Call by Value。在CVF生成动态链接库时,默认的通信协议为’_StdCall’,其参数传递方式是第一种。而在Delphi中,参数的传递方式跟参数本身的类型相关。如有一子过程定义:

Procedure Sub_Name(Const x1,x2:Double; Var x3,x4:Double);

这里,x1,x2被定义为常数类型的双精度实数,其参数传递方式为第二种,即Call by value,其值在传递中保持不变;x3, x4为变量类型的双精度整数,其传递方式为第一种,即Call by Reference,其值在计算中可以被改变*。在默认情况下,即参数前面没有说明属于那种类型的参数,则默认为常数类型。

也可以采用Delphi中的指针变量来获得与FORTRAN默认条件下完全相同的调用方式:

Procedure Sub_Name(x1,x2:Pointer;Var x3,x4:Double);

Pointer表明x1,x2是无类型指针(也可以采用有类型指针,这里从略),这时如果在另一程序中定义了四个双精度数a1,a2,a3,a4,按如下方式调用:

   Sub_Name(@a1,@a2,a3,a4)

a1,a2,a3,a4中的值在计算前后都是可以改变的。

以指针变量作为虚参对于DelphiFORTRAN的混合编程中数组的传递很有意义。因为Delphi的数组和FORTRAN中不同,Delphi中固定的数组和动态数组的传递方式是不相同的。采用指针,就没有那么费事。如上例中,如果a1,a2是数组,那么其调用方式为:

    Sub_Name(@a1[0],@a2[0],a3,a4)

就是将第一个数组元素的地址传递过去。如果不是第一个元素的地址,是后面某个元素的地址,则虚实数组的结合从这个元素开始。

如果虚参中出现了字符串,则有两种传递方式。一种是根据FORTRAN中的标准,同时传递字符串内容和长度,这时,FORTRAN中的子程序定义为:

Subroutine Sub_Name(x1,x2,Str,x3, x4)

DECATTRIBUTES DLLEXPORT::SUB_NAME
         DECATTRIBUTES ALIAS:’Sub_Name’::SUB_NAME

  Real(8),Dimension(:)::x1,x2

  Character(Len=*) Str

  Real(8),Intent(Out)::x3,x4

Delphi中相应的接口过程定义为:

Procedure Sub_Name(Const x1,x2:Pointer;

Str:String;

Len:Integer;

 Var x3, x4:Double);

则按如下方式调用:

  Sub_Name(@a1[0],@a2[0],Str,Length(Str),a3,a4);

另一种方式更为简便,因为在Delphi中,字符串被视为动态数组,以Call by Refence方式传递,因此,可先在FORTRAN中使用编译字将字符串的传递方式强制为地址传递方式,即:

Subroutine Sub_Name(x1,x2,Str,x3, x4)

DECATTRIBUTES DLLEXPORT::SUB_NAME
         DECATTRIBUTES ALIAS:’Sub_Name’::SUB_NAME

  Real(8),Dimension(:)::x1,x2

  Character(Len=*) Str

  Real(8),Intent(Out)::x3,x4

DEC$ Attributes Refence::Str

这时,Delphi中相应的接口过程定义为:

Procedure Sub_Name(Const x1,x2:Pointer; Str:String; Var x3, x4:Double);

则按如下方式调用:

 Sub_Name(@a1[0],@a2[0],Str, a3,a4);

第二种混合编程方式的实现过程是:首先在FORTRAN子程序按上述方法定义好,并编译成动态链接库ForSub.Dll;然后在Delphi中按如下方法定义动态链接库子过程接口。在接口区(Interface)中定义过程首部:

Procedure Sub_Name(Const x1,x2 : Pointer;

 Str : String;

Var x3, x4 : Double); Stdcall;

在实现区(Implementation)中加上以下语句:

Procedure Sub_Name; external ' ForSub.dll' name ' Sub_Name ';

这样,在Delphi程序设计中就可以像调用自己的子程序一样调用Sub_Name了。

由于FOR90引入了模块(Module)单元,可以将一些相关的数据和方法封装在模块里,因此,对一个模块中不同的子程序进行上述的定义,则在一个动态链接库中获得多个可被外部程序调用的子程序。在Delphi中要调用这些子程序,对每一个都需要编写Delphi中的子过程接口。



* FOR90中借鉴了这种区分输入输出参数的定义方式,引入了关键字Intent,在参数定义中,Intent(in)表示该参数在传递过程中是不改变的,Intent(Out)表示是可以改变的,并且FOR90中规定得更为严格。

 

 

阅读(3741) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册