摘 要:介绍了C++和Fortran 90混合语言编程中,在C++中调用Fortran动态连接库(DLL,Dynamic Link Library)的方法以及参量传递、函数调用等的方法。
关键词:混合编程,Fortran 90,C++,动态连接库
前言
混合语言编程是利用二种或二种以上编程语言编写的源码构建程序模块的过程。混合语言编程有以下优点:(1)调用已经存在的源码,如许多单位以前的计算机程序大部分都是用Fortran语言编写的,为了有效地利用现有的程序,减少不必要的重复性工作,可把原有的源码编译成动态连接库(Dynamic Link Library, DLL),以便于其它编程语言的调用;(2)利用某语言处理某些问题特有的速度和优势,如Fortran语言具有强大的科学计算速度和能力,Visual C++、C++ Builder、Visual Basic、Power Builder或Delphi具有良好的图形用户界面。
DLL提供了一种在Windows环境下共享代码和在应用程序间进行通信的方法。DLL可以用多种语言编写,只要遵循一定的规范,不同语言编写的DLL可以互相调用。在工程开发中,利用图形用户界面友好的开发工具作为前端开发工具,并在其中调用由其它语言实现的具有特殊功能的DLL模块,可提高工程开发质量和效率。本文介绍C++ Builder中调用DLL的方法,其中的DLL是用Fortran语言编写的。
调用DLL的方法
2.1 调用DLL的方式
C++应用程序中调用DLL中的函数有两种方式:静态引入方式和动态引入方式[1]。
2.1.1 静态引入方式
静态引入方式,就是在编译连接阶段就已经确定了要调用的函数程序,运行时自动加载相应的DLL,直到程序结束前,该DLL始终存在于内存中。静态加载一个DLL,方法比较简单,只需在应用程序连接时,把DLL相应的库文件加入到应用程序工程中。虽然这种引入DLL的方式简单,但它有很大的缺陷,如DLL加载后,就一直驻留在内存中,即使DLL已经不再使用。而动态引入方式,没有这些缺陷。下面重点介绍动态引入DLL的方法。
2.1.2 动态引入方式
动态引入DLL,需要用到三个API函数,它们是LoadLibrary函数、GetProcAddress函数和FreeLibrary函数。
(1)LoadLibrary函数
LoadLibrary函数的原型为:HINSTANCE LoadLibrary(LPCTSTR lpLibFileName)。lpLibFileName用于指定要装载DLL文件的路径和文件名。如果函数执行成功,则返回装载模块的实例句柄。否则,返回一个小于HINSTANCEERROR的错误代码。如果在应用程序中用LoadLibrary调用某一个模块前,其它应用程序已经把该模块装入内存,则LoadLibrary并不装载该模块的另一个实例,而只是使该模块的“引用计数”加1。LoadLibrary函数用于匹配(Map)被调用模块,返回被装载模块的实例句柄用于GetProcAddress函数中。
(2)GetProcAddress函数
该函数得到给定模块中函数的地址,原型为:FARPROC GetProcAddress(HMODULE hModule, LPTCSTR lpProcName)。hModule包含被调用的函数库模块的句柄,这个值由LoadLibrary函数返回。如果把hModule设置为NULL,则表示要引用当前模块。lpProcName是指向含有函数名的以NULL结尾的字符串的指针,或者是函数的次序值。如果lpProcName参数是次序值,则如果该次序值的函数在模块中并不存在时,GetProcAddress仍返回一个非NULL值,这将引起混乱。因此大部分情况下用函数名是一种更好的选择。如果用函数名,则函数名的拼写必须与DLL文件EXPORTS中的对应并且函数名的大小写拼写一致。FARPROC实际上是一个无类型指针(LPVOID),当得到相应的函数地址后,要使用该地址时,应强制类型变换。如果函数执行成功,则返回模块中函数入口处的地址,否则返回NULL。
(3)FreeLibrary函数
该函数从内存中移出库模块,原型为:void FreeLibrary(HMODULE hModule)。hModule为库模块的句柄,这个值由LoadLibrary返回。由于库模块在内存中只装载一次,因而调用FreeLibrary首先使库模块的引用计数减少1。如果引用数减为0,则卸出该模块。每调用一次LoadLibrary就应调用一次FreeLibrary,以保证不会有多余的库模块在应用程序结束后仍留在内存中。
2.2 DLL调用协定的一致
混合语言编程要遵守函数名一致、参数名一致、函数类型匹配、参数类型匹配以及参数传递等规则。
对于C++中调用Fortran语言编写的DLL,函数名一致,是指调用函数和被调用函数不仅名称一致,而且大小写一致。参数名一致,也是这样。函数类型匹配和参数类型匹配是指调用函数和被调用函数中类型一致或相协调。
参数传递的类型要一一匹配。单个参数传递有两种方式:值传递和地址(引用)传递(传递参数的地址)。其它有关具体细节可参见C++和Fortran帮助。
应用实例
3.1 Fortran DLL的编写
用Visual Fortran 6.0编写Fortran DLL相当简单。在Projects类型里选择“Fortran Dynamic Link Library”建立“A DLL application with exported symbols”工程,即可生成一个有DLL输出标识的工程。对于原有的源码,只需在原有的Fortran源码中,加入DLL输出标识,编译生成DLL即可。Fortran语言的函数分为两类:SUBROUTINE和FUNCTION,两种函数都可被另一种语言中调用函数的调用。下面给出一个简单的Fortran DLL源码的例子,并附有必要的注释。
! Fortran DLL 源码
! Test.f90
!
! FUNCTIONS/SUBROUTINES exported from TEST.dll:
! TEST - subroutine
!
! 1到N累加,用S返回累加值
subroutine SUM(N,S)
! Expose subroutine SUM to users of this DLL
!
!MS$ ATTRIBUTES DLLEXPORT::SUM !DLL输出SUM函数
!MS$ ATTRIBUTES VALUE :: N !值传递
!MS$ ATTRIBUTES REFERENCE :: S !值引用
! Variables
INTEGER N
REAL S
! Body of SUM
S=0
DO 10 I=1,N
S=S+I
10 CONTINUE
end subroutine SUM
! 求绝对值
REAL FUNCTION MYABS(X) !函数类型为实型(REAL)
! Expose function MYABS to users of this DLL
!
!MS$ ATTRIBUTES DLLEXPORT::MYABS !DLL输出MYABS函数
!MS$ ATTRIBUTES VALUE :: X !值传递
! Variables
REAL X
! Body of MYABS
IF(X.LT.0)THEN
MYABS = -1*X
RETURN
ELSE
MYABS = X
END IF
END
! 两个数相加
REAL FUNCTION ADD(X, Y) !函数类型为实型(REAL)
! Expose function ADD to users of this DLL
!
!MS$ ATTRIBUTES DLLEXPORT::ADD !DLL输出ADD函数
!MS$ ATTRIBUTES VALUE :: X !值传递
!MS$ ATTRIBUTES VALUE :: Y !值传递
! Variables
REAL X, Y
! Body of ADD
ADD = X + Y
END
3.2 C++调用 Fortran DLL
以下是C++ Builder中调用Fortran DLL的源码:
Unit1.h:
//---------------------------------------------------------------------------#ifndef Unit1H#define Unit1H//---------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *DllTest;
TLabel *Label1;
TLabel *Label2;
TLabel *Label3;
void __fastcall DllTestClick(TObject *Sender);
private: // User declarations
float res1, res2, res3;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------#include <vcl.h>#pragma hdrstop#include "Unit1.h"//------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------
fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------
void __fastcall TForm1::DllTestClick(TObject *Sender)
{
typedef UINT (CALLBACK* LPFNDLLFUNC1)(int, float *);
typedef float (CALLBACK* LPFNDLLFUNC2)(float);
typedef float (CALLBACK* LPFNDLLFUNC3)(float, float);
HINSTANCE hDLL; // Handle to DLL
LPFNDLLFUNC1 lpfnDllFunc1; // Function pointer
LPFNDLLFUNC2 lpfnDllFunc2; // Function pointer
LPFNDLLFUNC3 lpfnDllFunc3; // Function pointer
hDLL = LoadLibrary("F:/Projects/Test/Debug/Test.dll");
if (hDLL != NULL)
{
lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "SUM"); //"DLLFunc1"
lpfnDllFunc2 = (LPFNDLLFUNC2)GetProcAddress(hDLL, "MYABS"); //"DLLFunc2"
lpfnDllFunc3 = (LPFNDLLFUNC3)GetProcAddress(hDLL, "ADD"); //"DLLFunc3"
if (!lpfnDllFunc1)
{
// handle the error
FreeLibrary(hDLL);
// return SOME_ERROR_CODE;
MessageDlg("The function does not exist.", mtWarning, TMsgDlgButtons() << mbYes, 0);
}
else
{
// call the function
for(int i=1;i<10;i++)
{
lpfnDllFunc1(i,&res1);
Label1->Caption=FloatToStr(res1);
}
}
if (!lpfnDllFunc2)
{
// handle the error
FreeLibrary(hDLL);
MessageDlg("The function does not exist.", mtWarning, TMsgDlgButtons() << mbYes, 0);
}
else
{
// call the function
for(float i=-21.5;i<-11.5;i++)
{
res2 = lpfnDllFunc2(i);
Label2->Caption=FloatToStr(res2);
}
}
if (!lpfnDllFunc3)
{
// handle the error
FreeLibrary(hDLL);
MessageDlg("The function does not exist.", mtWarning, TMsgDlgButtons() << mbYes, 0);
}
else
{
// call the function
res3 = lpfnDllFunc3(-8.5, 15.8);
Label3->Caption=FloatToStr(res3);
}
}
}
//---------------------------------------------------------------------
结束语
本文介绍了C++ Builder中调用Fortran语言编写的DLL的方法,并给出了一个具体的实例帮助读者了解其方法。事实上,用某种语言编写的DLL,可被其它多种语言调用,以实现多种语言的混合编程,达到充分利用现有的源码、各种语言的优势,提高工程开发质量和效率的目的。
参考文献 刘文圣、刘光、权元聪编著,C++ Builder指南,人民邮电出版社,1999。
|