2

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)
Ting Lei
  • 91
  • 9
  • This isn't related to the problem (as given in the answer below), but the arguments to the call to `deb_cc` are default real/integer rather than explicitly the C-interoperable kinds. The kinds are the same (or the compiler would complain) but for maximum portability consider declaring the variables with those interoperable kinds. Note also that the first `use iso_c_binding` has no effect because of this. – francescalus Mar 18 '15 at 21:42
  • Fracescalus, do you mean I should use the c_int and so on in the main fortran program? Thanks – Ting Lei Mar 18 '15 at 22:49
  • It is what I would do, not just for those times where `real` is not the same as `real(c_float)` but for clarity. Where they are the same, though, it isn't necessary (yet still doesn't hurt). – francescalus Mar 18 '15 at 22:57
  • Got your point. Thanks a lot. – Ting Lei Mar 18 '15 at 23:04

1 Answers1

4

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; 
}
  • Thank you so much. Yes. Modified the codes as you pointed out, it works now. However, for my actual goal, I need to use the pointers (rh in the c++ sub, defined with 3 *) in the form of 3d arrays. This is the way it is used in the original c++ codes. Why it doesn't work now when It was called from the fortran? Did I miss something? Your futher clarificaiton is appreciated. – Ting Lei Mar 18 '15 at 22:31
  • BTW: I'm sorry for the intendation problems. – Ting Lei Mar 18 '15 at 22:31
  • Where are the arrays actually created, in C++ or in Fortran? These two forms can be incompatible. Fortran requires contiguous arrays to send them or receive them from C. – Vladimir F Героям слава Mar 18 '15 at 22:32
  • The actual array x1 was created in the fortran as an allocatable arrays. The arugument in c++ is defined as the ***rh (pointer to pointer to pointer). is that kind of pointers able to be used as the 3d arrays? – Ting Lei Mar 18 '15 at 22:42
  • You have to set-up a sequence of pointers to the rows of the big array yourself inside C++, you can't pass that (easily) from Fortran. See `array2` in http://c-faq.com/aryptr/dynmuldimary.html – Vladimir F Героям слава Mar 18 '15 at 22:55
  • Vladimir, Thanks a lot. That is interesting. Seem such setup done in the c++ main (calling the sub) can be inherited while it won't work when the sub is called from fortran. Another important lesson to me. Thanks again. – Ting Lei Mar 19 '15 at 00:51