Fortran与C/C++混合编程示例

以下例子均来自网络,只是稍作了编辑,方便今后查阅。

   子目录

(一) Fortran调用C语言

(二) C语言调用Fortran

(三) C++ 调用Fortran

(四) Fortran 调用 C++

需要说明的是,(一)和(二)对GCC编译器的版本要求并不高;而(三)和(四)对GCC编译器的要求比较高,需要GCC在4.7及以上才能编译通过,这是由于自Fortran 2003一代语言起,增加了一个名为“iso_c_binding”的模块,可以很方便地在Fortran和C++之间传递参数,简化了两者的混合编程。

————————————————————我是分割线———————————————————————————————

(一) Fortran调用C语言

源程序来自网络,增加了一个二维数组类型的调用示例。

Fortran文件(main.f90)的内容如下:

    program main
          implicit none
          interface 
                subroutine sub_c(n1,n2,n3)
                      integer :: n1
                      real :: n2
                      real(8) :: n3
                end subroutine

                real(8) function func_d(var2d)
                      real(8) :: var2d(2,3)
                end function
                
                real(8) function func_c(n3)
                      real(8) :: n3
                end function
                

                
          end interface
          
          integer :: n1
          real :: n2
          real(8) :: n3,n4
          real(8) :: var2d(2,3)
          
          n1=3
          n2=5.0
          call sub_c(n1,n2,n3)
          n4=func_c(n3)
          write(*,*) "n1=",n1
          write(*,*) "n2=",n2
          write(*,*) "n3=",n3
          write(*,*) "n4=",n4
          
          var2d=0
          var2d(1,1)=dble(n1)
          var2d(1,2)=dble(n2)          
          write(*,*) "var2d(1,:): ",var2d(1,:)
          write(*,*) "var2d(2,:): ",var2d(2,:)
          n2=func_d(var2d)
          write(*,*) "var2d(1,:): ",var2d(1,:)
          write(*,*) "var2d(2,:): ",var2d(2,:)
           
                  
    end program main

C文件(sub.c)的内容如下:

#include <math.h>
#include <stdio.h>

void sub_c_(int *,float *,double *);
double func_c_(double *);
double func_d_(double [][2]);


void sub_c_(int *n1,float *n2,double *n3)
{
      *n3=pow(*n2,*n1);
}

double func_c_(double *n3)
{
      double n4;
      n4=sqrt(*n3);
      return n4;
}

double func_d_(double var2d[3][2])
{
      double NN;
      NN=77.0;
      printf("%5.2f %5.2f %5.2f 
",var2d[0][0],var2d[1][0],var2d[2][0]);
      printf("%5.2f %5.2f %5.2f 
",var2d[0][1],var2d[1][1],var2d[2][1]);
      var2d[1][0]=NN;
      printf("%5.2f %5.2f %5.2f 
",var2d[0][0],var2d[1][0],var2d[2][0]);
      printf("%5.2f %5.2f %5.2f 
",var2d[0][1],var2d[1][1],var2d[2][1]);
      return NN;
}

Makefile 文件的内容如下:

all: 
    gcc -c sub.c -o sub.o
    gfortran -c main.f90 -o main.o 
    gfortran -o main.exe main.o sub.o

    
clean:
    rm *.o *.exe

编译并运行的结果如下:

[She@she-centos7 TestABC]$ make
gcc -c sub.c -o sub.o
gfortran -c main.f90 -o main.o 
gfortran -o main.exe main.o sub.o
[She@she-centos7 TestABC]$ ./main.exe 
 n1=           3
 n2=   5.00000000    
 n3=   125.00000000000000     
 n4=   11.180339887498949     
 var2d(1,:):    3.0000000000000000        5.0000000000000000        0.0000000000000000     
 var2d(2,:):    0.0000000000000000        0.0000000000000000        0.0000000000000000     
 3.00  5.00  0.00 
 0.00  0.00  0.00 
 3.00 77.00  0.00 
 0.00  0.00  0.00 
 var2d(1,:):    3.0000000000000000        77.000000000000000        0.0000000000000000     
 var2d(2,:):    0.0000000000000000        0.0000000000000000        0.0000000000000000     

注意二维数组在Fortran和C中存储的方式不同,Fortran中是“列优先”,而C中是“行优先”

在参数传递时,容易导致两者的引用错误。使用时要务必注意这个区别!!!

(二) C语言调用Fortran

示例1. 用结构体在C和Fortran之间传递参数

这种方法不需要对函数体进行预先声明,直接引用即可。由于结构体本身的最小存储单元的限制,在结构体内部的数据类型最好先作优化,以节省内存开销,原理可参见C语言结构体占用空间内存大小解析

c2fortran.c

    #include <string.h>  
      
    struct test{  
        int n;  
        float m;  
    };  
      
    /* c2fortran.c */  
    int main(int argc,char *argv[])  
    {  
         struct test t;  
         t.n=90;  
         t.m=0.003;  
         int i;  
         float e = 2.71828;  
         char hello[32];  
         int length = sizeof(hello);  
         strcpy(hello,"Hello Fortran from C");  
         for(i=strlen(hello); i<length; i++)  
              hello[i] = ' ';  
         showhie_(hello,&length,&e,&t);  
         return(0);  
    }  

showhie.f

C   showhie.f  
C  
          SUBROUTINE SHOWHIE(HELLO,LENGTH,E,T)  
          CHARACTER*(*) HELLO  
          INTEGER LENGTH  
          REAL E  
          TYPE TEST  
              INTEGER N  
              REAL M  
          END TYPE  
          TYPE (TEST) :: T  
C  
          WRITE(*,100) HELLO(1:LENGTH),LENGTH,E,T%N,T%M  
100       FORMAT(3X,A,2X,I3,4X,F7.5,2X,I3,2X,F7.5)  
          RETURN  
          END SUBROUTINE SHOWHIE  

Makefile_test 的内容:

    fortran2c : c2fortran.o showhie.o  
    gfortran c2fortran.o showhie.o -o c2fortran  
      
    showhie.o : showhie.f  
    gfortran -c showhie.f -o showhie.o  
      
    c2fortran.o : c2fortran.c  
    gcc -c c2fortran.c -o c2fortran.o  
      
    clean :   
    rm *.o  

编译并运行的结果如下:

[She@she-centos7 TestABC]$ make -f Makefile_test 
gcc -c c2fortran.c -o c2fortran.o  
gfortran -c showhie.f -o showhie.o  
gfortran c2fortran.o showhie.o -o c2fortran  
[She@she-centos7 TestABC]$ ./c2fortran 
   Hello Fortran from C               32    2.71828   90  0.00300

示例2. 用指针变量在C和Fortran之间传递参数  

main.c

#include <stdio.h>

void sub_fortran_(int *,float *,double *);
double function_fortran_(double *);

int main()
{
      int num_int;
      float num_float;
      double num_double;
      double num;
      num_int=3;
      num_float=5.0;
      sub_fortran_(&num_int,&num_float,&num_double);
      num=function_fortran_(&num_double);
      printf("num_int=%d
num_float=%f
num_double=%f
num=%f",num_int,num_float,num_double,num);
      return 0;
}

sub.f90

subroutine Sub_Fortran(NumInt,NumFloat,NumDouble)
      implicit none
      integer :: NumInt
      real :: NumFloat
      real(8) :: NumDouble
      NumDouble=NumFloat**NumInt
end subroutine

real(8) function Function_Fortran(NumDouble)
      implicit none
      real(8) :: NumDouble
!      Function_Fortran=sqrt(NumDouble)  ! 用gfortran编译报错,pgf90也同样无法编译,不知何故...
      Function_Fortran=NumDouble**0.5
end function

Makefile_CcallFortran

all:
    gcc –o main.o –c main.c
    gfortran –o sub.o –c sub.f90
    gcc –o main2.exe main.o sub.o

clean:
    rm *.o main2.exe

编译及运行结果:

num=15625.000000[She@she-centos7 TestABC]$ make -f Makefile_CcallFortran 
gcc -o main.o -c main.c
gfortran -o sub.o -c sub.f90
gcc -o main2.exe main.o sub.o
sub.o:在函数‘function_fortran_’中:
sub.f90:(.text+0x75):对‘sqrt’未定义的引用
collect2: 错误ld 返回 1
make: *** [all] 错误 1

[She@she-centos7 TestABC]$ make -f Makefile_CcallFortran 
gcc -o main.o -c main.c
gfortran -o sub.o -c sub.f90
gcc -o main2.exe main.o sub.o
[She@she-centos7 TestABC]$ ./main2.exe
num_int=3
num_float=5.000000
num_double=125.000000
num=15625.000000

(三) C++ 调用Fortran

示例1. 调用Fortran函数生成一个NxN的矩阵

本例要求:gcc 和 gfortran 的编译器版本为4.7及以上

fortran_matrix_multiply.f90

subroutine matrix_multiply(A, rows_A, cols_A, B, rows_B, cols_B, C, rows_C, cols_C) bind(c)
     use iso_c_binding
     integer(c_int) :: rows_A, cols_A, rows_B, cols_B, rows_C, cols_C
     real(c_double) :: A(rows_A, cols_A), B(rows_B, cols_B), C(rows_C, cols_C)
 
     C = matmul(A, B)
end subroutine matrix_multiply

cpp_main_1.cpp

#include <iostream>
#include <vector>
#include <random>
#include <ctime>
 
 using namespace std;
 
 //Fortran subroutine definition "translated" to C++
 extern "C" {
     void matrix_multiply(double *A, int *rows_A, int *cols_A, double *B, int *rows_B, int *cols_B, double *C, int *rows_C, int *cols_C);
 }
 
 //Fill a vector with random numbers in the range [lower, upper]
 void rnd_fill(vector<double> &V, double lower, double upper) {
 
     //use system clock to create a random seed
     size_t seed (clock());
 
     //use the default random engine and an uniform distribution
     default_random_engine eng(seed);
     uniform_real_distribution<double> distr(lower, upper);
 
     for( auto &elem : V){
         elem = distr(eng);
     }
 }
 
 //Print matrix V(rows, cols) storage in column-major format
 void print_matrix(vector<double> const &V, int rows, int cols) {
 
     for(int i = 0; i < rows; ++i){
         for(int j = 0; j < cols; ++j){
             std::cout << V[j * rows + i] << " ";
         }
         std::cout << std::endl;
     }
     std::cout << std::endl;
 }
 
 int main() {
     size_t N = 3;
     vector<double> A(N * N), B(N * N), C(N * N);
 
     //Fill A and B with random numbers in the range [0,1]:
     rnd_fill(A, 0.0, 1.0);
     rnd_fill(B, 0.0, 1.0);
 
     //Multiply matrices A and B, save the result in C
     int sz = N;
     matrix_multiply(&A[0], &sz, &sz, &B[0], &sz, &sz, &C[0], &sz, &sz);
 
     //print A, B, C on the standard output
     print_matrix(A, sz, sz);
     print_matrix(B, sz, sz);
     print_matrix(C, sz, sz);
 
     return 0;
 }

Makefile

all:
    gfortran -c fortran_matrix_multiply.f90
    g++ -c -std=c++11 cpp_main_1.cpp
    gfortran fortran_matrix_multiply.o cpp_main_1.o -lstdc++ -o mix_example.out

clean:
    rm *.o *.out

编译及运行结果如下:

[She@she-centos7 eg2]$ make
gfortran -c fortran_matrix_multiply.f90
g++ -c -std=c++11 cpp_main_1.cpp
gfortran fortran_matrix_multiply.o cpp_main_1.o -lstdc++ -o mix_example.out
[She@she-centos7 eg2]$ ./mix_example.out 
0.131538 0.678865 0.0345721 
0.45865 0.934693 0.5297 
0.218959 0.519416 0.00769819 

0.131538 0.678865 0.0345721 
0.45865 0.934693 0.5297 
0.218959 0.519416 0.00769819 

0.336233 0.741784 0.364408 
0.60501 1.46015 0.515041 
0.268717 0.638137 0.282764 

本机的 gcc 和 gfortran 的编译器版本为4.8:

[She@she-centos7 eg2]$ gfortran -v
使用内建 specs。
COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
目标:x86_64-redhat-linux
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
线程模型:posix
gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) 
[She@she-centos7 eg2]$ gcc -v
使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
目标:x86_64-redhat-linux
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
线程模型:posix
gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) 

需要注意的是,这个程序对 gcc 和 gfortran 的版本比较敏感。它在CentOS 6.5 x64 & gcc version 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC) 上测试时,编译报错,它并不支持 g++ 的 "-std=c++11" 选项,它自带的 C++ 编译选项为 “-std=c++98”,无法方便地实现 C++ 和 Fortran 之间的调用。

(四) Fortran 调用 C++

示例1. 来自《FORTRAN90 Program Calls C++ Function》

主程序 kronrod_prb.f90

program main

!*****************************************************************************80
!
!! MAIN is the main program for KRONROD_PRB.
!
!  Licensing:
!
!    This code is distributed under the GNU LGPL license. 
!
!  Modified:
!
!    01 September 2011
!
!  Author:
!
!    John Burkardt
!
  use, intrinsic :: iso_c_binding! Fortran 2003 语言新加的模块。

  implicit none
!
!  TIMESTAMP is provided by the C++ library, and so the following
!  INTERFACE block must set up the interface.
!
  interface
    subroutine timestamp ( ) bind ( c )
      use iso_c_binding
end subroutine timestamp
  end interface

  call timestamp ( )

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) 'KRONROD_PRB:'
  write ( *, '(a)' ) '  FORTRAN90 version'
  write ( *, '(a)' ) '  FORTRAN90 test program calls C++ functions.'

  call test01 ( )
  call test02 ( )
!
!  Terminate.
!
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) 'KRONROD_PRB:'
  write ( *, '(a)' ) '  Normal end of execution.'

  write ( *, '(a)' ) ' '
  call timestamp ( )

  stop
end
subroutine test01 ( )

!*****************************************************************************80
!
!! TEST01 tests the code for the odd case N = 3.
!
!  Licensing:
!
!    This code is distributed under the GNU LGPL license. 
!
!  Modified:
!
!    29 November 2010
!
!  Author:
!
!    John Burkardt
!
  use, intrinsic :: iso_c_binding

  implicit none
!
!  KRONROD is provided by the C++ library, and so the following
!  INTERFACE block must be set up to describe how data is to 
!  be passed.
!
  interface
    subroutine kronrod ( n, eps, x, w1, w2 ) bind ( c )
      use iso_c_binding
      integer ( c_int ), VALUE :: n
      real ( c_double ), VALUE :: eps
      real ( c_double ) :: x(*)
      real ( c_double ) :: w1(*)
      real ( c_double ) :: w2(*)
    end subroutine kronrod
  end interface

  integer ( c_int ), parameter :: n = 3

  real ( c_double ) eps
  integer ( c_int ) i
  integer ( c_int ) i2
  real ( c_double ) s
  real ( c_double ) w1(n+1)
  real ( c_double ) w2(n+1)
  real ( c_double ) :: wg(n) = (/ &
    0.555555555555555555556D+00, &
    0.888888888888888888889D+00, &
    0.555555555555555555556D+00 /)
  real    ( c_double ) :: wk(2*n+1) = (/ &
    0.104656226026467265194D+00, &
    0.268488089868333440729D+00, &
    0.401397414775962222905D+00, &
    0.450916538658474142345D+00, &
    0.401397414775962222905D+00, &
    0.268488089868333440729D+00, &
    0.104656226026467265194D+00 /)
  real ( c_double ) x(n+1)
  real ( c_double ) :: xg(n) = (/ &
   -0.77459666924148337704D+00, &
    0.0D+00, &
    0.77459666924148337704D+00 /)
  real ( c_double ) :: xk(2*n+1) = (/ &
   -0.96049126870802028342D+00, &
   -0.77459666924148337704D+00, &
   -0.43424374934680255800D+00, &
    0.0D+00, &
    0.43424374934680255800D+00, &
    0.77459666924148337704D+00, &
    0.96049126870802028342D+00 /)

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) 'TEST01'
  write ( *, '(a)' ) '  Request KRONROD to compute the Gauss rule'
  write ( *, '(a)' ) '  of order 3, and the Kronrod extension of'
  write ( *, '(a)' ) '  order 3+4=7.'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  Compare to exact data.'

  eps = 0.000001D+00

  call kronrod ( n, eps, x, w1, w2 )

  write ( *, '(a)' ) ' '
  write ( *, '(a,i2)' ) '  KRONROD returns 3 vectors of length ', n + 1
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '     I      X               WK              WG'
  write ( *, '(a)' ) ' '
  do i = 1, n + 1
    write ( *, '(2x,i4,2x,g14.6,2x,g14.6,2x,g14.6)' ) i, x(i), w1(i), w2(i)
  end do

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '               Gauss Abscissas'
  write ( *, '(a)' ) '            Exact           Computed'
  write ( *, '(a)' ) ' '
  do i = 1, n
    if ( 2 * i <= n + 1 ) then
      i2 = 2 * i
      s = -1.0D+00
    else
      i2 = 2 * ( n + 1 ) - 2 * i
      s = +1.0D+00
    end if
    write ( *, '(2x,i4,2x,g14.6,2x,g14.6)' ) i, xg(i), s * x(i2)
  end do
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '               Gauss Weights'
  write ( *, '(a)' ) '            Exact           Computed'
  write ( *, '(a)' ) ' '
  do i = 1, n
    if ( 2 * i <= n + 1 ) then
      i2 = 2 * i
    else
      i2 = 2 * ( n + 1 ) - 2 * i
    end if
    write ( *, '(2x,i4,2x,g14.6,2x,g14.6)' ) i, wg(i), w2(i2)
  end do

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '             Gauss Kronrod Abscissas'
  write ( *, '(a)' ) '            Exact           Computed'
  write ( *, '(a)' ) ' '
  do i = 1, 2 * n + 1
    if ( i <= n + 1 ) then
      i2 = i
      s = -1.0D+00
    else
      i2 = 2 * ( n + 1 ) - i
      s = +1.0D+00
    end if
    write ( *, '(2x,i4,2x,g14.6,2x,g14.6)' ) i, xk(i), s * x(i2)
  end do
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '             Gauss Kronrod Weights'
  write ( *, '(a)' ) '            Exact           Computed'
  write ( *, '(a)' ) ' '
  do i = 1, 2 * n + 1
    if ( i <= n + 1 ) then
      i2 = i
    else
      i2 = 2 * ( n + 1 ) - i
    end if
    write ( *, '(2x,i4,2x,g14.6,2x,g14.6)' ) i, wk(i), w1(i2)
  end do

  return
end
subroutine test02 ( )

!*****************************************************************************80
!
!! TEST02 tests the code for the even case N = 4.
!
!  Licensing:
!
!    This code is distributed under the GNU LGPL license. 
!
!  Modified:
!
!    29 November 2010
!
!  Author:
!
!    John Burkardt
!
  use, intrinsic :: iso_c_binding

  implicit none
!
!  KRONROD is provided by the C++ library, and so the following
!  INTERFACE block must be set up to describe how data is to 
!  be passed.
!
  interface
    subroutine kronrod ( n, eps, x, w1, w2 ) bind ( c )
      use iso_c_binding
      integer ( c_int ), VALUE :: n
      real ( c_double ), VALUE :: eps
      real ( c_double ) :: x(*)
      real ( c_double ) :: w1(*)
      real ( c_double ) :: w2(*)
    end subroutine kronrod
  end interface

  integer ( c_int ), parameter :: n = 4

  real ( c_double ) eps
  integer ( c_int ) i
  integer ( c_int ) i2
  real ( c_double ) s
  real ( c_double ) w1(n+1)
  real ( c_double ) w2(n+1)
  real ( c_double ) x(n+1)

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) 'TEST02'
  write ( *, '(a)' ) '  Request KRONROD to compute the Gauss rule'
  write ( *, '(a)' ) '  of order 4, and the Kronrod extension of'
  write ( *, '(a)' ) '  order 4+5=9.'

  eps = 0.000001D+00

  call kronrod ( n, eps, x, w1, w2 )

  write ( *, '(a)' ) ' '
  write ( *, '(a,i2)' ) '  KRONROD returns 3 vectors of length ', n + 1
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '     I      X               WK              WG'
  write ( *, '(a)' ) ' '
  do i = 1, n + 1
    write ( *, '(2x,i4,2x,g14.6,2x,g14.6,2x,g14.6)' ) i, x(i), w1(i), w2(i)
  end do

  return
end

C++函数 kronrod.cpp

#include <cstdlib>
#include <iostream>
#include <cmath>
#include <ctime>
#include "kronrod.hpp"

using namespace std;



//****************************************************************************80

void abwe1 ( int n, int m, double eps, double coef2, bool even, double b[], 
  double *x, double *w )

//****************************************************************************80
//
//  Purpose:
//
//    ABWE1 calculates a Kronrod abscissa and weight.
//
//  Licensing:
//
//    This code is distributed under the GNU LGPL license.
//
//  Modified:
//
//    03 August 2010
//
//  Author:
//
//    Original FORTRAN77 version by Robert Piessens, Maria Branders.
//    C++ version by John Burkardt.
//
//  Reference:
//
//    Robert Piessens, Maria Branders,
//    A Note on the Optimal Addition of Abscissas to Quadrature Formulas
//    of Gauss and Lobatto,
//    Mathematics of Computation,
//    Volume 28, Number 125, January 1974, pages 135-139.
//
//  Parameters:
//
//    Input, int N, the order of the Gauss rule.
//
//    Input, int M, the value of ( N + 1 ) / 2.
//
//    Input, double EPS, the requested absolute accuracy of the
//    abscissas.
//
//    Input, double COEF2, a value needed to compute weights.
//
//    Input, bool EVEN, is TRUE if N is even.
//
//    Input, double B[M+1], the Chebyshev coefficients.
//
//    Input/output, double *X; on input, an estimate for
//    the abscissa, and on output, the computed abscissa.
//
//    Output, double *W, the weight.
//
{
  double ai;
  double b0;
  double b1;
  double b2;
  double d0;
  double d1;
  double d2;
  double delta;
  double dif;
  double f;
  double fd;
  int i;
  int iter;
  int k;
  int ka;
  double yy;

  if ( *x == 0.0 )
  {
    ka = 1;
  }
  else
  {
    ka = 0;
  }
//
//  Iterative process for the computation of a Kronrod abscissa.
//
  for ( iter = 1; iter <= 50; iter++ )
  {
    b1 = 0.0;
    b2 = b[m];
    yy = 4.0 * (*x) * (*x) - 2.0;
    d1 = 0.0;

    if ( even )
    {
      ai = m + m + 1;
      d2 = ai * b[m];
      dif = 2.0;
    }
    else
    {
      ai = m + 1;
      d2 = 0.0;
      dif = 1.0;
    }

    for ( k = 1; k <= m; k++ )
    {
      ai = ai - dif;
      i = m - k + 1;
      b0 = b1;
      b1 = b2;
      d0 = d1;
      d1 = d2;
      b2 = yy * b1 - b0 + b[i-1];
      if ( !even )
      {
        i = i + 1;
      }
      d2 = yy * d1 - d0 + ai * b[i-1];
    }

    if ( even )
    {
      f = ( *x ) * ( b2 - b1 );
      fd = d2 + d1;
    }
    else
    {
      f = 0.5 * ( b2 - b0 );
      fd = 4.0 * ( *x ) * d2;
    }
//
//  Newton correction.
//
    delta = f / fd;
    *x = *x - delta;

    if ( ka == 1 )
    {
      break;
    }

    if ( r8_abs ( delta ) <= eps )
    {
      ka = 1;
    }
  }
//
//  Catch non-convergence.
//
  if ( ka != 1 )
  {
    cout << "
";
    cout << "ABWE1 - Fatal error!
";
    cout << "  Iteration limit reached.
";
    cout << "  Last DELTA was " << delta << "
";
    exit ( 1 );
  }
//
//  Computation of the weight.
//
  d0 = 1.0;
  d1 = *x;
  ai = 0.0;
  for ( k = 2; k <= n; k++ )
  {
    ai = ai + 1.0;
    d2 = ( ( ai + ai + 1.0 ) * ( *x ) * d1 - ai * d0 ) / ( ai + 1.0 );
    d0 = d1;
    d1 = d2;
  }

  *w = coef2 / ( fd * d2 );

  return;
}
//****************************************************************************80

void abwe2 ( int n, int m, double eps, double coef2, bool even, double b[], 
  double *x, double *w1, double *w2 )

//****************************************************************************80
//
//  Purpose:
//
//    ABWE2 calculates a Gaussian abscissa and two weights.
//
//  Licensing:
//
//    This code is distributed under the GNU LGPL license.
//
//  Modified:
//
//    03 August 2010
//
//  Author:
//
//    Original FORTRAN77 version by Robert Piessens, Maria Branders.
//    C++ version by John Burkardt.
//
//  Reference:
//
//    Robert Piessens, Maria Branders,
//    A Note on the Optimal Addition of Abscissas to Quadrature Formulas
//    of Gauss and Lobatto,
//    Mathematics of Computation,
//    Volume 28, Number 125, January 1974, pages 135-139.
//
//  Parameters:
//
//    Input, int N, the order of the Gauss rule.
//
//    Input, int M, the value of ( N + 1 ) / 2.
//
//    Input, double EPS, the requested absolute accuracy of the
//    abscissas.
//
//    Input, double COEF2, a value needed to compute weights.
//
//    Input, bool EVEN, is TRUE if N is even.
//
//    Input, double B[M+1], the Chebyshev coefficients.
//
//    Input/output, double *X; on input, an estimate for
//    the abscissa, and on output, the computed abscissa.
//
//    Output, double *W1, the Gauss-Kronrod weight.
//
//    Output, double *W2, the Gauss weight.
//
{
  double ai;
  double an;
  double delta;
  int i;
  int iter;
  int k;
  int ka;
  double p0;
  double p1;
  double p2;
  double pd0;
  double pd1;
  double pd2;
  double yy;

  if ( *x == 0.0 )
  {
    ka = 1;
  }
  else
  {
    ka = 0;
  }
//
//  Iterative process for the computation of a Gaussian abscissa.
//
  for ( iter = 1; iter <= 50; iter++ )
  {
    p0 = 1.0;
    p1 = *x;
    pd0 = 0.0;
    pd1 = 1.0;
    ai = 0.0;
    for ( k = 2; k <= n; k++ )
    {
      ai = ai + 1.0;
      p2 = ( ( ai + ai + 1.0 ) * (*x) * p1 - ai * p0 ) / ( ai + 1.0 );
      pd2 = ( ( ai + ai + 1.0 ) * ( p1 + (*x) * pd1 ) - ai * pd0 ) 
        / ( ai + 1.0 );
      p0 = p1;
      p1 = p2;
      pd0 = pd1;
      pd1 = pd2;
    }
//
//  Newton correction.
//
    delta = p2 / pd2;
    *x = *x - delta;

    if ( ka == 1 )
    {
      break;
    }

    if ( r8_abs ( delta ) <= eps )
    {
      ka = 1;
    }
  }
//
//  Catch non-convergence.
//
  if ( ka != 1 )
  {
    cout << "
";
    cout << "ABWE2 - Fatal error!
";
    cout << "  Iteration limit reached.
";
    cout << "  Last DELTA was " << delta << "
";
    exit ( 1 );
  }
//
//  Computation of the weight.
//
  an = n;

  *w2 = 2.0 / ( an * pd2 * p0 );

  p1 = 0.0;
  p2 = b[m];
  yy = 4.0 * (*x) * (*x) - 2.0;
  for ( k = 1; k <= m; k++ )
  {
    i = m - k + 1;
    p0 = p1;
    p1 = p2;
    p2 = yy * p1 - p0 + b[i-1];
  }

  if ( even )
  {
    *w1 = *w2 + coef2 / ( pd2 * (*x) * ( p2 - p1 ) );
  }
  else
  {
    *w1 = *w2 + 2.0 * coef2 / ( pd2 * ( p2 - p0 ) );
  }

  return;
}
//****************************************************************************80

void kronrod ( int n, double eps, double x[], double w1[], double w2[] )

//****************************************************************************80
//
//  Purpose:
//
//    KRONROD adds N+1 points to an N-point Gaussian rule.
//
//  Discussion:
//
//    This subroutine calculates the abscissas and weights of the 2N+1
//    point Gauss Kronrod quadrature formula which is obtained from the 
//    N point Gauss quadrature formula by the optimal addition of N+1 points.
//
//    The optimally added points are called Kronrod abscissas.  The 
//    abscissas and weights for both the Gauss and Gauss Kronrod rules
//    are calculated for integration over the interval [-1,+1].
//
//    Since the quadrature formula is symmetric with respect to the origin,
//    only the nonnegative abscissas are calculated.
//
//    Note that the code published in Mathematics of Computation 
//    omitted the definition of the variable which is here called COEF2.
//
//  Storage:
//
//    Given N, let M = ( N + 1 ) / 2.  
//
//    The Gauss-Kronrod rule will include 2*N+1 points.  However, by symmetry,
//    only N + 1 of them need to be listed.
//
//    The arrays X, W1 and W2 contain the nonnegative abscissas in decreasing
//    order, and the weights of each abscissa in the Gauss-Kronrod and
//    Gauss rules respectively.  This means that about half the entries
//    in W2 are zero.
//
//    For instance, if N = 3, the output is:
//
//    I      X               W1              W2
//
//    1    0.960491        0.104656         0.000000   
//    2    0.774597        0.268488         0.555556    
//    3    0.434244        0.401397         0.000000
//    4    0.000000        0.450917         0.888889
//
//    and if N = 4, (notice that 0 is now a Kronrod abscissa)
//    the output is
//
//    I      X               W1              W2
//
//    1    0.976560        0.062977        0.000000   
//    2    0.861136        0.170054        0.347855    
//    3    0.640286        0.266798        0.000000   
//    4    0.339981        0.326949        0.652145    
//    5    0.000000        0.346443        0.000000
//
//  Licensing:
//
//    This code is distributed under the GNU LGPL license.
//
//  Modified:
//
//    03 August 2010
//
//  Author:
//
//    Original FORTRAN77 version by Robert Piessens, Maria Branders.
//    C++ version by John Burkardt.
//
//  Reference:
//
//    Robert Piessens, Maria Branders,
//    A Note on the Optimal Addition of Abscissas to Quadrature Formulas
//    of Gauss and Lobatto,
//    Mathematics of Computation,
//    Volume 28, Number 125, January 1974, pages 135-139.
//
//  Parameters:
//
//    Input, int N, the order of the Gauss rule.
//
//    Input, double EPS, the requested absolute accuracy of the
//    abscissas.
//
//    Output, double X[N+1], the abscissas.
//
//    Output, double W1[N+1], the weights for 
//    the Gauss-Kronrod rule.
//
//    Output, double W2[N+1], the weights for 
//    the Gauss rule.
//
{
  double ak;
  double an;
  double *b;
  double bb;
  double c;
  double coef;
  double coef2;
  double d;
  bool even;
  int i;
  int k;
  int l;
  int ll;
  int m;
  double s;
  double *tau;
  double x1;
  double xx;
  double y;

  b = new double[((n+1)/2)+1];
  tau = new double[(n+1)/2];
  
  m = ( n + 1 ) / 2;
  even = ( 2 * m == n );

  d = 2.0;
  an = 0.0;
  for ( k = 1; k <= n; k++ )
  {
    an = an + 1.0;
    d = d * an / ( an + 0.5 );
  }
//
//  Calculation of the Chebyshev coefficients of the orthogonal polynomial.
//
  tau[0] = ( an + 2.0 ) / ( an + an + 3.0 );
  b[m-1] = tau[0] - 1.0;
  ak = an;

  for ( l = 1; l < m; l++ )
  {
    ak = ak + 2.0;
    tau[l] = ( ( ak - 1.0 ) * ak 
      - an * ( an + 1.0 ) ) * ( ak + 2.0 ) * tau[l-1] 
      / ( ak * ( ( ak + 3.0 ) * ( ak + 2.0 ) 
      - an * ( an + 1.0 ) ) );
    b[m-l-1] = tau[l];

    for ( ll = 1; ll <= l; ll++ )
    {
      b[m-l-1] = b[m-l-1] + tau[ll-1] * b[m-l+ll-1];
    }
  }

  b[m] = 1.0;
//
//  Calculation of approximate values for the abscissas.
//
  bb = sin ( 1.570796 / ( an + an + 1.0 ) );
  x1 = sqrt ( 1.0 - bb * bb );
  s = 2.0 * bb * x1;
  c = sqrt ( 1.0 - s * s );
  coef = 1.0 - ( 1.0 - 1.0 / an ) / ( 8.0 * an * an );
  xx = coef * x1;
//
//  Coefficient needed for weights.
//
//  COEF2 = 2^(2*n+1) * n// * n// / (2n+1)//
//
  coef2 = 2.0 / ( double ) ( 2 * n + 1 );
  for ( i = 1; i <= n; i++ )
  {
    coef2 = coef2 * 4.0 * ( double ) ( i ) / ( double ) ( n + i );
  }
//
//  Calculation of the K-th abscissa (a Kronrod abscissa) and the
//  corresponding weight.
//
  for ( k = 1; k <= n; k = k + 2 )
  {
    abwe1 ( n, m, eps, coef2, even, b, &xx, w1+k-1 );
    w2[k-1] = 0.0;

    x[k-1] = xx;
    y = x1;
    x1 = y * c - bb * s;
    bb = y * s + bb * c;

    if ( k == n )
    {
      xx = 0.0;
    }
    else
    {
      xx = coef * x1;
    }
//
//  Calculation of the K+1 abscissa (a Gaussian abscissa) and the
//  corresponding weights.
//
    abwe2 ( n, m, eps, coef2, even, b, &xx, w1+k, w2+k );

    x[k] = xx;
    y = x1;
    x1 = y * c - bb * s;
    bb = y * s + bb * c;
    xx = coef * x1;
  }
//
//  If N is even, we have one more Kronrod abscissa to compute,
//  namely the origin.
//
  if ( even )
  {
    xx = 0.0;
    abwe1 ( n, m, eps, coef2, even, b, &xx, w1+n );
    w2[n] = 0.0;
    x[n] = xx;
  }

  delete [] b;
  delete [] tau;

  return;
}

//****************************************************************************80

double r8_abs ( double x )

//****************************************************************************80
//
//  Purpose:
//
//    R8_ABS returns the absolute value of an R8.
//
//  Licensing:
//
//    This code is distributed under the GNU LGPL license. 
//
//  Modified:
//
//    14 November 2006
//
//  Author:
//
//    John Burkardt
//
//  Parameters:
//
//    Input, double X, the quantity whose absolute value is desired.
//
//    Output, double R8_ABS, the absolute value of X.
//
{
  double value;

  if ( 0.0 <= x )
  {
    value = x;
  } 
  else
  {
    value = - x;
  }
  return value;
}
//****************************************************************************80

void timestamp ( )

//****************************************************************************80
//
//  Purpose:
//
//    TIMESTAMP prints the current YMDHMS date as a time stamp.
//
//  Example:
//
//    31 May 2001 09:45:54 AM
//
//  Licensing:
//
//    This code is distributed under the GNU LGPL license. 
//
//  Modified:
//
//    08 July 2009
//
//  Author:
//
//    John Burkardt
//
//  Parameters:
//
//    None
//
{
# define TIME_SIZE 40

  static char time_buffer[TIME_SIZE];
  const struct std::tm *tm_ptr;
  size_t len;
  std::time_t now;

  now = std::time ( NULL );
  tm_ptr = std::localtime ( &now );

  len = std::strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm_ptr );

  std::cout << time_buffer << "
";

  return;
# undef TIME_SIZE
}

C++头文件依赖 kronrod.hpp

extern "C" 
{
  void abwe1 ( int n, int m, double eps, double coef2, bool even, double b[], 
    double *x, double *w );
  void abwe2 ( int n, int m, double eps, double coef2, bool even, double b[], 
    double *x, double *w1, double *w2 );
  void kronrod ( int n, double eps, double x[], double w1[], double w2[] );
  double r8_abs ( double x );
  void timestamp ( );
}

两个版本的Makefile全文

注意:两个版本的Makefile,都可以正常运行并获得结果。区别在于版本一的Makefile 无法对这个.cpp文件进行正常调试,比如无法识别 pgdbg 中的 print 命令等。

Makefile 版本一(cpp文件无法调试

all:
    g++ -g -c -std=c++11 kronrod.cpp -o kronrod.o
    pgf90 -g -c kronrod_prb.f90 -o kronrod_prb.o
    pgf90 -g -lstdc++ -o main.exe kronrod.o kronrod_prb.o

clean:
    rm *.o *.exe

编译及运行过程如下:

[She@she-centos7 F90_call_CPP_eg1]$ make clean && make
rm *.o *.exe
g++ -g -c -std=c++11 kronrod.cpp -o kronrod.o
pgf90 -g -c kronrod_prb.f90 -o kronrod_prb.o
pgf90 -g -lstdc++ -o main.exe kronrod.o kronrod_prb.o
[She@she-centos7 F90_call_CPP_eg1]$ pgdbg ./main.exe

pgdbg 调试这个 .cpp 文件,断点设在第 101 行:

pgdbg> Loaded: /home/She/Programs/TestABC/F90_call_CPP_eg1/./main.exe
MAIN_
pgdbg> (1) breakpoint set at: _IO_marker::abwe1 line: "kronrod.cpp"@101 address: 0x40150F 

pgdbg> Breakpoint at 0x40150F, function _IO_marker::abwe1, file kronrod.cpp, line 101
 #101:           ai = m + m + 1;

pgdbg> print ai
ERROR: Cannot evaluate [0x709a9d8] DWARF_CALL_FRAME_CFA: 

pgdbg> print m
ERROR: Cannot evaluate [0x7088e60] DWARF_CALL_FRAME_CFA: 

pgdbg> q

Makefile 版本二(cpp文件可以正常调试

all:
    pgc++ -g -c -std=c++11 kronrod.cpp -o kronrod.o
    pgf90 -g -c kronrod_prb.f90 -o kronrod_prb.o
    pgf90 -g -lstdc++ -o main.exe kronrod.o kronrod_prb.o

clean:
    rm *.o *.exe

编译及调试运行过程如下:

[She@she-centos7 F90_call_CPP_eg1]$ make clean && make
rm *.o *.exe
pgc++ -g -c -std=c++11 kronrod.cpp -o kronrod.o
"kronrod.cpp", line 636: warning: variable "len" was set but never used
    size_t len;
           ^

pgf90 -g -c kronrod_prb.f90 -o kronrod_prb.o
pgf90 -g -lstdc++ -o main.exe kronrod.o kronrod_prb.o
[She@she-centos7 F90_call_CPP_eg1]$ pgdbg ./main.exe

pgdbg 调试这个 .cpp 文件,断点同样设在第 101 行:

PGI Debugger 17.4-0 x86-64 (Workstation, 16 Process)
PGI Compilers and Tools
Copyright (c) 2017, NVIDIA CORPORATION.  All rights reserved.

pgdbg> Loaded: /home/She/Programs/TestABC/F90_call_CPP_eg1/./main.exe
MAIN_
pgdbg> (1) breakpoint set at: abwe1 line: "kronrod.cpp"@101 address: 0x401765

pgdbg> Breakpoint at 0x401765, function abwe1, file kronrod.cpp, line 101
 #101:           ai = m + m + 1;

pgdbg> print m
2
pgdbg>

不带调试参数"-g"的直接编译及运行结果:

[She@she-centos7 F90_call_CPP_eg1]$ make
g++ -c -std=c++11 kronrod.cpp -o kronrod.o
pgf90 -c kronrod_prb.f90 -o kronrod_prb.o
pgf90 -lstdc++ -o main.exe kronrod.o kronrod_prb.o
[She@she-centos7 F90_call_CPP_eg1]$ ./main.exe
09 June 2017 02:51:39 PM
 
KRONROD_PRB:
  FORTRAN90 version
  FORTRAN90 test program calls C++ functions.
 
TEST01
  Request KRONROD to compute the Gauss rule
  of order 3, and the Kronrod extension of
  order 3+4=7.
 
  Compare to exact data.
 
  KRONROD returns 3 vectors of length  4
 
     I      X               WK              WG
 
     1    0.960491        0.104656         0.00000    
     2    0.774597        0.268488        0.555556    
     3    0.434244        0.401397         0.00000    
     4     0.00000        0.450917        0.888889    
 
               Gauss Abscissas
            Exact           Computed
 
     1   -0.774597       -0.774597    
     2     0.00000        -0.00000    
     3    0.774597        0.774597    
 
               Gauss Weights
            Exact           Computed
 
     1    0.555556        0.555556    
     2    0.888889        0.888889    
     3    0.555556        0.555556    
 
             Gauss Kronrod Abscissas
            Exact           Computed
 
     1   -0.960491       -0.960491    
     2   -0.774597       -0.774597    
     3   -0.434244       -0.434244    
     4     0.00000        -0.00000    
     5    0.434244        0.434244    
     6    0.774597        0.774597    
     7    0.960491        0.960491    
 
             Gauss Kronrod Weights
            Exact           Computed
 
     1    0.104656        0.104656    
     2    0.268488        0.268488    
     3    0.401397        0.401397    
     4    0.450917        0.450917    
     5    0.401397        0.401397    
     6    0.268488        0.268488    
     7    0.104656        0.104656    
 
TEST02
  Request KRONROD to compute the Gauss rule
  of order 4, and the Kronrod extension of
  order 4+5=9.
 
  KRONROD returns 3 vectors of length  5
 
     I      X               WK              WG
 
     1    0.976560        0.629774E-01     0.00000    
     2    0.861136        0.170054        0.347855    
     3    0.640286        0.266798         0.00000    
     4    0.339981        0.326949        0.652145    
     5     0.00000        0.346443         0.00000    
 
KRONROD_PRB:
  Normal end of execution.
 
09 June 2017 02:51:39 PM
Warning: ieee_inexact is signaling
FORTRAN STOP
原文地址:https://www.cnblogs.com/snake553/p/6962386.html