C/C++调用Fortran的使用说明

C/C++调用Fortran的使用说明

[日期:2010-11-22] 来源:C/C++ 作者:C/C++ [字体:大 中 小]

这里将详细介绍一下在C++中如何调用用Fortran语言编写函数的问题,即Fortran与C++的混合编程问题。

通常情况下,C++与Fortran的混合编程问题是利用动态链接库的方式进行的,换句话说,如果在C/C++中调用Fortran函数,应将Fortran函数按照一定的协议编译为动态链接库,然后即可实现二者的混合编程问题。实现这一目的有两种方式:显示链接与隐式链接,下面通过两个非常简单的实例分别介绍之。
编译环境:Fortran:推荐使用Compaq Visual Fortran 6.0以上的版本,C++:Microsoft Visual C++6.0。

1.   显式链接,
(a) 找开CVF编译器,然后新建一个Fortran DLL工程(选择Fortran Dynamic Link Library),并指定工程名,如下图所示:


按确定按钮,然后新建一个Fortran 源文件,并输入以下的Fortran代码:
(这里仅为说明问题,实际的问题比下面的代码要复杂得多。)
SUBROUTINE OUTPUT(N)

!必须声明本函数为输出函数:DLLEXPORT
!DEC$ ATTRIBUTES DLLEXPORT::OUTPUT  

IMPLICIT NONE
INTEGER N

N=N+10
WRITE(*,*) "N=",n
END SUBROUTINE OUTPUT

经编译,链接后,将在Debug目录下生成两个文件,即dll文件与lib文件。
(b) 启动Visual C++6.0,然后新建一个console工程(即Win32 Console Application,当然在MFC中也是完成可行的),如下图所示:


按确定按钮,新建一个空的工程后,再新建一个C++源文件,并输入以下的代码:
#include
#include

int main()
{
typedef void (_stdcall * wndProc)(int& );
 
HINSTANCE hLibrary=LoadLibrary("pp.dll"); //加载动态库文件
if(hLibrary==NULL)
{
cout<<"can't find the dll file"<<endl;
return -1;
}

wndProc test=(wndProc)GetProcAddress(hLibrary,"OUTPUT"); //获得Fortran导出函数的地址
if(test==NULL)
{
cout<<"can't find the function file."<<endl;
return -2;
}

int n=3;
test(n); //调用fortran函数

FreeLibrary(hLibrary); //卸载动态库文件

return 0;
}
当编译通过后,将由fortran编译器生成的动态库文件(本算例为pp.dll)复制到C++的Debug文件夹中(即mm\debug),然后点击执行即可,程序的动行结果为:


2.   隐式链接
与上文所介绍的显示链接相比,隐式链接要相对容易一点。下面也通过一个算例进行说明。
(1) 建立一个Fortran动态库文件,其方法与上述完全相同,然后在CVF编译器中输入以下的代码:

SUBROUTINE OUTPUT(N)

!下面为对Fortran函数的声明
!ms$if .not. defined(LINKDIRECT)
!ms$attributes dllexport :: OUTPUT
!ms$endif 

IMPLICIT NONE
INTEGER N

N=N+10
WRITE(*,*) "N=",n
END SUBROUTINE OUTPUT

编译通过后,将在Fortran工程文件夹的Debug文件夹中生成两个文件,即*.dll文件与*.lib文件。
(2) 新建一个C++ Console项目,并新建一个C++文件,然后输入以下的代码:
#include <iostream.h>
#include <windows.h>

//声明函数OUTPUT为extern型的,即是从外部调用的。
extern "C" void _stdcall OUTPUT(int& n);

int main()
{
int n=3;
OUTPUT(n);

return 0;
}

然后将Fortran编译器生成的两个文件(dll文件与lib文件)复制到C++的当前目录下,并将lib文件加入到当前的C++工程项目中,如下图所示:
点击菜单“工程-添加工程-Files”,然后选中pp.lib文件即可,如下图所示:

此时即可通过编译,执行此程序,其输出结果如下所示:


总结
本文通过两个简单的实例详细介绍了如何在C++中调用Fortran函数的两种方法,即显式链接与隐式链接。当然实际中我们所遇到的问题将会比本实例要复杂得多,本文仅作为抛砖引玉之用。显式链接与隐式链接两种方法均各自有其优缺点,但由于隐式链接要比显示链接容易得多,也易于理解,实际的大部分算例均采用这种方式。


在linux中操作

对于通常的用户,接促C语言的较多,但是,C语言的开发者,有时还想利用高效的Fortran 数据包 或者是Fortran语言的开发者想借用C提供的强大辅助功能,为此,为了方便大家对二语言相互调用的学习。这里给出了一些简单的入门性技术介绍。
    1. C 调用Fortran
    编辑Fortran 源文件 add.f95
    subroutine add(a, b, c)
    implicit none
    real:: a, b, c
    !
    ! the add routine
    ! c = a+b
    !
    c = a + b
    write(*,*) a, ’+’, b, ’=’, c
    return
    end subroutine add
    这里部介绍Fortran的语法格式,add.f95的功能是建立一个add routine 也就是函数。
    编译 add.f95
    f95 -o add.o -c add.f95
    编辑C 主程序main.c
      1 #include
      2
      3 extern void add_(float *a, float *b, float *c);
      4
      5 int main(int argc, char *argvs[])
      6 {
      7         float a, b, c;
      8         a = 5.0;
      9         b = 7.0;
     10         add_(&a, &b, &c);
     11
     12         return 0;
     13 }
    3: 声明你要调用的函数,这里注意,是add_,调用的时候用指针,即变量地址,你应该明白了Fortran的调用是改变参数值的。
    10:调用fortran函数
    编译与执行
    #gfortran -o main main.c add.o
    #./main
       5.000000     +   7.000000     =   12.00000
    NOTES; if you use the gcc tool, it will generate error
    #gcc -o main main.c add.o
    add.o: In function `add_’:
    add.f95:(.text+0x4c): undefined reference to `_gfortran_st_write’
    add.f95:(.text+0x69): undefined reference to `_gfortran_transfer_real’
    add.f95:(.text+0x87): undefined reference to `_gfortran_transfer_character’
    add.f95:(.text+0xa4): undefined reference to `_gfortran_transfer_real’
    add.f95:(.text+0xc2): undefined reference to `_gfortran_transfer_character’
    add.f95:(.text+0xdf): undefined reference to `_gfortran_transfer_real’
    add.f95:(.text+0xed): undefined reference to `_gfortran_st_write_done’
    collect2: ld returned 1 exit status
    Some Wrong with your PATH for you gcc library

    OK! you are clever and has known how call fortran routines in the C progaram. Next part I show you how to call C routines in the Fortran program!


    2, Fortran 调用 C 函数
    编辑C源码add.c
      1 #include
      2
      3 void add_(float *a, float *b, float *c)
      4 {
      5         *c = *a + *b;
      6         printf(\"%f + %f = %f\\n\", *a, *b, *c);
      7 }
    Notes The funtion name \"add_\"
    编译源代码
    # gcc -o add.o -c add.c
    编辑fortran 主程序main.f95,编译和执行
      1 PROGRAM MAIN
      2   implicit none [Page]
      3   real:: i, j, k
      4   !
      5   ! The first fortran program
      6   !
      7   i = 5.0
      8   j = 8.0
      9   call add(i, j, k)
     10   stop
     11 end program MAIN
    NOTE 9 line
    # gfortran -o main main.f95 add.o (or f95 -o main main.f95 add.o)
    # ./main
    5.000000 + 8.000000 = 13.000000
 

OK. If you reach here, congratulations, you have learned how to call routines between C and fortran!



例程:

Calling Fortran routines from C++

Example-1: Calling routines and functions

The following sample shows how Fortran routines and functions can be called from a C++ program.

(1) The C++ file:

//    This illustrates how a Fortran routine and function may be
//    called from a main program in C++
#include
extern "C"
{
       void __stdcall FR1(int*,int *);
       int __stdcall FF1(int *);
}
int main()
{
       int n=10,nSq,nCube;
       FR1(&n,&nSq);
       cout << "The square is:" << nSq << endl;
       nCube=FF1(&n);
       cout << "The Cube is:" << nCube << endl;
       return 0;
}

(2) The Fortran file:

         SUBROUTINE FR1(N,M)
C       COMPUTES THE SQUARE OF N, RETURNS IN M    
         M=N*N
         RETURN
         END
C
         INTEGER FUNCTION FF1(N)
C       COMPUTES THE CUBE OF N
         FF1=N*N*N
         RETURN
         END

Back to Top
Example-2: Passing C char string to a Fortran routine

The following sample shows how a C char string may be passed from a C++ program to a Fortran routine.

(1) The C++ file:

//    This illustrates how a Fortran routine may be
//    called from a main program in C++, and a char[] string passed to it
#include
#include
extern "C"
{
       void __stdcall FR1(int *,int *,char *);
}
int main()
{
     int n=10,nSq;
char szCtest[20];
strcpy(szCtest,"teststring");
FR1(&n,&nSq,szCtest);
cout << "The square is:" << nSq << endl;
         return 0;
}

(2) The Fortran file:

         SUBROUTINE FR1(N,M,CSTR)
INTEGER*4 CSTR(1)
C       HERE WE RECEIVE THE C CHAR STRING IN AN INTEGER ARRAY
C       COULD ALSO HAVE USED A BYTE ARRAY
M=N*N
WRITE(6,20) (CSTR(L),L=1,3)
     20    formAT(' CSTR=',3A4)
         WRITE(6,*) 'DONE'
RETURN
END

Back to Top
Example-3: Passing arrays to a Fortran routine

The following sample shows how arrays may be passed from a C++ program to a Fortran routine.

(1) The C++ file:

    // Illustrate passing integer and floating point arrays
    // from C++ to Fortran
    #include
    extern "C"
    {
        int __stdcall SUMIT(int *,int*);
        float __stdcall MEAN(float*,int*);
    }
    int main()
    {
        int iA[]={3,5,6,7,2,3,4,5,11,7},iN=10,iSum;
        float fpA[]={1.2f,3.f,44.f,2.5f,-1.3f,33.44f,5.f,0.3f,-3.6f,24.1f},fpMean;
        iSum=SUMIT(iA,&iN);
        cout << "The Sum of iA is:" << iSum << endl;
        fpMean=MEAN(fpA,&iN);
        cout << "The Mean of fpA is:" << fpMean << endl;
        return 0;
    }

(2) The Fortran file:

       INTEGER FUNCTION SUMIT(IA,N)
       INTEGER IA(1)
       ISUM=0
       DO 50 J=1,N
50    ISUM=ISUM+IA(J)
       SUMIT=ISUM
       RETURN
       END
C
       REAL FUNCTION MEAN(RA,N)
       REAL RA(1)
       SUM=0.
       DO 50 J=1,N
50    SUM=SUM+RA(J)
       IF(N.GT.0) MEAN=SUM/FLOAT(N)
       RETURN
       END

Back to Top
Calling C++ routines from Fortran

The following examples work with Microsoft Visual C++ and Compaq Visual Fortran. Your mileage may vary on other systems.

Example-1: Calling routines and functions

The following sample shows how C++ routines and functions can be called from a Fortran program.

(1) The Fortran file:

       INTEGER CR2
       N=10
       CALL CR1(N,M)
       WRITE(6,20) N,M
20    formAT(' The square of',I3,' is',I4)
       K=CR2(N)
       WRITE(6,30) N,K
30    formAT(' The cube of',I3,' is',I15)
       CALL EXIT
       END

(2) The C++ files:

       extern "C"
       {
void __stdcall CR1(int *,int *);
int __stdcall CR2(int *);
       }
       void __stdcall CR1(int *n, int *m)
       {
// Compute the square of n, return in m
int k;
k=*n;
*m=k*k;
return;
       }
       int __stdcall CR2(int *n)
       //    Compute the cube of n
       {
         int m,k;
k=*n;
         m=k*k*k;
return m;
       }

Back to Top
Further Reading

These are some other sources of information.

Digital Visual Fortran Programmer's Guide, esp. the chapter titled "Programming with Mixed Languages". This online book is included with all recent versions of the compiler. The book is also available for download in PDF format by clicking here.
Mixed-Language Issues (from Microsoft)
Also see Microsoft's C Calls to Fortran page.
Mixed Language Programming using C++ and Fortran 77 by Carsten A. Arnholm has many examples.
Mixed Language Programming - Fortran and C by Allan, Chipperfield and Warren-Smith is another good source.
FTN95 Mixed Language Programming from the University of Salford.
A Comparison of C++, Fortran 90 and Oberon-2 for Scientific Programming by Bernd Mösli.
Interfacing Fortran and C by Janne Saarela.
Mixed Language Programming from Pittsburgh Supercomputing Center.
Some examples from DEC:
Visual C/C++ Calling Visual Fortran DLL Example
Visual Fortran Calling Visual C Example
Visual Basic - Visual Fortran DLL Example
Will C++ be faster than Fortran? by T.L.Veldhuizen and M.E.Jernigan.
Interfacing Absoft Fortran with C.
Mixing ANSI-C with Fortran 77 or Fortran 90 by B. Einarsson.
Comparison of C++ and Fortran 90 for Object Oriented Scientific Programming by J.Cary, S.Shasharina, J.Cummings, J.Reynders and P.Hinker.
Fortran and C Programming from Iowa State University.
Win32 Fortran Compiler Comparisons by John Appleyard.
Calling Fortran Routines from C/C++ by J. Thornburg.
f2c - Fortran to C Converter

------------------------------file:a.cpp-------------
#include
#include
extern "C"
{
void _stdcall TESTA(int *);
}
void main()
{
     double   a = sin(1.00);
     cout<<"a="<      int ii=1;
     TESTA(&ii);
}

------------------------------file:b.f-------------
SUBROUTINE TESTA(IA)
WRITE(*,*) "IA=", IA
RETURN
END

编译提示错误:
Linking...
dfor.lib(matherr.obj) : error LNK2005: __matherr already defined in LIBCD.lib(matherr.obj)
LINK : warning LNK4098: defaultlib "libc.lib" conflicts with use of other libs; use /NODEFAULTLIB:library
Debug/StaggeredGrid.exe : fatal error LNK1169: one or more multiply defined symbols found
Error executing link.exe.

经过分析发现是因为在main函数里面调用了sin数学函数,解决的方法是添加文件头:
#pragma comment (lib, "dfor")



你可能感兴趣的:(C/C++调用Fortran的使用说明)