Search code examples
c++fortranmixing

the run time aborting when calling c++ sub from fortran


I had read many posts here about mixing languages use of Fortran and C++. However, I'm still stuck with my current problem: my Fortran program always aborted.


I have the Fortran program: test-cc.f90 and the C++ program: deb_cc.cc.

deb_cc.cc contains:

#include <iostream>
using namespace std;
extern "C" void deb_cc_(float*** rh,int* x, int* y , int* z_ext )
{
  cout <<"thinkdeb 1"<<endl;
  int i, j, k;
  cout <<"thinkdeb 1"<<endl;
  cout <<"thinktest i=8,j=4,k=1"<< " (*x) " << (*x)<<endl;
  cout <<"thinktest i=8,j=4,k=1"<< " x3/rh " << rh[1][1][1]<<endl; //abortion                        
                                                                //      here 
  cout <<"thinkdeb 7"<<endl;
  return;
}//end function

test-cc.f90 contains:

    use ISO_C_BINDING

    implicit none

    interface
      subroutine  deb_cc( rh,x,y,z_ext)
        use ISO_C_BINDING
        implicit none
        real(c_float),allocatable::rh(:,:,:)
        integer(c_int):: x,y,z_ext
      end subroutine
    end interface

    integer nx,ny,nz
    parameter (nx=10,ny=10,nz=10)
    real  ,dimension (:,:,:),allocatable:: x1
    integer:: iy1,iy2,iy3,iy4
    integer i,j,k

    allocate(x1(nx,ny,nz))

    do k=1,nz
      do j=1,ny
        do i=1,nx
          x1(i,j,k)=k*1000+j*100+i
        enddo
      enddo
    enddo

    iy1=nx
    iy2=ny
    iy3=nz

    call deb_cc(x1,iy1,iy2,iy3)

  end

I compiled them by pgf90 -c test-cc.f90 and pgcpp -c deb_cc.cc Finally, I linked them by pgf90 -pgcpplibs test-cc.o deb_cc.o. The output is:

 thinktest in test- x1 (8,2,2) is     2208.000
 thinkdeb 1
 thinkdeb 1
 thinktest i=8,j=4,k=1 (*x) 10
 Segmentation fault (core dumped)

Solution

  • You use the iso_c_binding module, but your procedure interface is not C interoperable.

    The iso_c_binding module is not the most important thing. The bind(C) attribute is the key. (I ranted several times about the unfortunate name of the tag here)

    You use an assumed shape allocatable array argument

    real(c_float),allocatable::rh(:,:,:)
    

    these are not allowed in interoperable procedures in Fortran 2008, because C or C++ have no idea what to do with them. They are not just addresses. If you used the bind(C) attribute in the interface, the compiler should tell you it is wrong.

    There is a possibility to pass them in the next Fortran standard (in an existing TS actually) using a special C header, but some compilers (notably gfortran) are still not compatible.

    As you do not do any reallocation on the C side (at least in your example), you can just pass the array as an assumed size (array(*)) argument. I also changed the C++ name, no need for the underscore.

    interface
      subroutine  deb_cc(rh,x,y,z_ext) bind(C, name="deb_cc")
        use ISO_C_BINDING
        real(c_float) :: rh(*)
        integer(c_int):: x,y,z_ext
       end subroutine
    end interface
    

    On the C side, you cannot use the C arrays which are pointers to pointers ([i][j][k]). What you receive from Fortran is a single block of memory. You also have to pass the array shape. At least in the first two Fortan dimensions.

    I would just use a macro to index the array in C.

    // adjust as needed, many variants possible
    #define IND(i,j,k) = i + (j-1) * nx + (k-1) * nx * ny
    // adjust as needed, many variants possible
    
    
    extern "C" void deb_cc(float *rh, int *nx, int *ny, int *nz) {
      cout <<"thinktest i=8,j=4,k=1"<< " x3/rh " << rh(IND(8,4,1))<<endl; 
    }