fortranfortran-iso-c-binding

Should I put 'bind(C)' next to the subroutine which is not defined in C side?


Say, I have a C++ functions that I have to implement into the Fortran library.

I decided to make my Fortran wrapper in a module, which goes like below.

(C++ side)

extern "C" void cpp_func1(int* n, int* array) { ... }

(Fortran side)

module cpp2fortran
  use ISO_C_BINDING
  implicit none

  interface
! (1)
    subroutine cpp_func1(n, array) bind(C)
      use iso_c_binding
      integer(C_INT)     , intent(in) :: n
      type(C_PTR) , value, intent(in) :: array
    end subroutine
  end interface  

contains

! (2)
    subroutine Utilize_cpp_func1( ... )  ! bind(C)
    use iso_c_binding

    ...

    call cpp_func1(...)

    ...

  end subroutine

end module

I marked numbers at two places, e.g. (1) and (2).

At the end of the (1), I definitely put bind(C), and without it the code fail to be compiled.

The thing is, should I put bind(C) at the end of (2)?

The code compiles/works just fine independent from the presence of bind(C) at the end of the line (2), but I found that some Fortran-like optimization do not work if I put bind(C).

For example, in normal Fortran without bind(C),

real, allocatable :: arr1(:)
real, allocatable :: arr2(:)

...

arr2 = arr1

...

would work fine. However, if I put bind(C), it does not work and I had to make some modification such as following

real, allocatable :: arr1(:)
real, allocatable :: arr2(:)

...

do i = 1, size
  arr2(i) = arr1(i)
enddo

...

If it's safe to remove bind(C) from the (2)-like Fortran wrapper, I would happy to remove it and exploit the concise optimization such as arr2 = arr1 rather than make lengthy code writing all do loops.


This is the minimum example in which bind(C) makes a problem. I used ifort to compile it.

module cbind
  use iso_c_binding
  implicit none
contains
(*) subroutine sub1(array) !bind(C)
    use iso_c_binding
    implicit none
    real, allocatable, intent(in) :: array(:)

    real(C_FLOAT), allocatable, target :: array_target(:)

    allocate( array_target(size(array,1)) )

    array_target = array

    print *, 'array'
    print *, array
    print *, 'array_target'
    print *, array_target

  end subroutine
end module

program test
  use iso_c_binding
  use cbind
  implicit none
  real, allocatable :: array(:)

  allocate( array(5) )

  array = 1.0

  call sub1(array)

end program

Without bind(C) at the line (*), the code just works fine with the desired output.

array
  1.000000       1.000000       1.000000       1.000000       1.000000  
array_target
  1.000000       1.000000       1.000000       1.000000       1.000000  

However if I put bind(C), the code segfaults with following output.

 array
   1.000000       1.000000       1.000000       1.000000       1.000000    
 array_target
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source   
cbinding1          00000000004051B3  Unknown               Unknown  Unknown
libpthread-2.17.s  00002AC983A5E5F0  Unknown               Unknown  Unknown
cbinding1          000000000045382A  Unknown               Unknown  Unknown
cbinding1          000000000042B967  Unknown               Unknown  Unknown
cbinding1          000000000040FCB3  Unknown               Unknown  Unknown
cbinding1          000000000040CA01  Unknown               Unknown  Unknown
cbinding1          0000000000403C1B  Unknown               Unknown  Unknown
cbinding1          00000000004037E2  Unknown               Unknown  Unknown
libc-2.17.so       00002AC983C8D505  __libc_start_main     Unknown  Unknown
cbinding1          00000000004036E9  Unknown               Unknown  Unknown


Solution

  • The BIND attribute for a procedure is necessary to make it C interoperable. Although it has other effects, the attribute is not necessary for a Fortran procedure to be able to make use of an interoperable procedure itself. More specifically, a procedure which is not interoperable may use a non-interoperable procedure: one is likely to use this very useful fact a lot.

    In your case (from what we see), you do not need bind(c) on the definition of Utilize_cpp_func1. This procedure itself is not required to be C interoperable.

    Coming to the concern about array_target = array, this simply is a compiler weakness in some versions of the Intel compiler. It is not present in the current release.

    Why does bind(c) make a difference in this example? Your dummy argument array is allocatable. An interoperable procedure may have an allocatable dummy argument only under Fortran 2008+TS29113 or Fortran 2018, not under Fortran 2003 or Fortran 2008: it's a relatively new feature of the language and susceptible to things going a bit wrong in its implementation (or when using interpretations of the language which don't allow this feature).