Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a Fortran int array to C++ by calling C++ function in Fortran

Tags:

c++

fortran

I am trying to call a C++ function in a Fortran subroutine. This C++ function is supposed to update an integer array. Here is a non-working code I wrote. What is the issue?

! Fortran function that calls a C++ function.

subroutine my_function()

      integer(4) ar(*)

      integer(4) get_filled_ar

      ! Need correct syntax here.
      ar = get_filled_ar()
end


// C++ function:

    extern "C" {
        void get_filled_ar(int *ar){
            ar[0] = 1;
            ar[1] = 10;
            ar[3] = 100;
        }
    }
like image 812
cppb Avatar asked Jan 22 '23 02:01

cppb


2 Answers

With Fortran 2003 there is a standard and thus platform and compiler independent way to call C from Fortran, and also any language that uses the C calling interface. Also to call Fortran from C. While the various compiler writers are gradually adding Fortran 2003 features and there are few complete 2003 compilers, the ISO C Binding has been available for some time in many compilers. The ISO C Binding works better than the previous ad hoc techniques, which were sometimes poorly documented, and varied between compilers and platforms. To call C from Fortran, you write an "interface" that tells the Fortran compiler that it should use the C calling conventions, and C types.

Here is an example. As Mike wrote, since the C++ function returns void, treat it in the Fortran as a subroutine and call it. Thus it doesn't need to be given a type. Also, somewhere in the Fortran you have to reserve storage for the array -- the easiest way is with a declaration with numeric value for the dimension. And you need a main program in some language.

program test_call_C

use iso_c_binding

implicit none

interface c_interface

   subroutine get_filled_ar (ar) bind (C, name = "get_filled_ar")

   use iso_c_binding

   implicit none

   integer (c_int), intent (out), dimension (*) :: ar

   end subroutine get_filled_ar

end interface c_interface


integer (c_int), dimension (0:3) :: ar

call get_filled_ar (ar)

write (*, *) "Fortran: ar:", ar

stop

end program test_call_C

and in C:

void get_filled_ar (
   int ar []
) {

   ar [0] = 1;
   ar [1] = 10;
   ar [2] = 100;

   return;

}

Example commands:

gcc -c get_filled_ar.c
gfortran get_filled_ar.o test_call_C.f90  -o test_call_C.exe
./test_call_C.exe

To call your C++ code, use the following commands. The name specified in the "bind" obviates the need for a trailing underscore so your C++ code works directly.

g++ -c cplusplus.cc
gfortran cplusplus.o test_call_C.f90  -o test_call_Cplusplus.exe
./test_call_Cplusplus.exe
like image 107
M. S. B. Avatar answered Jan 24 '23 15:01

M. S. B.


Here's a full program with your code in it that should work (at least on linux with gcc/gfortran).

In the fortran file fortran.f, put:

     IMPLICIT NONE      
     CALL my_function()
     END

! FORTRAN function that calls a C++ function

     subroutine my_function()
! Tell fortran about the C++ function:
       external get_filled_ar

       integer ar(4)

! As the C++ function returns void, call it as a subroutine in
! fortran:
       call get_filled_ar(ar)
! To check it worked, print out the numbers:
       do i = 1,4
          write(*,*) i,"->",ar(i)
       enddo
     end

In cplusplus.cc put:

extern "C"
{
  void get_filled_ar_(int* ar) // note extra underscore to keep linker happy
  {
    ar[0] = 1;   
    ar[1] = 10;
    ar[3] = 100;
  }    
}

Then build (on linux with gcc/gfortran, might be different on other systems) with:

gfortran -c fortran.f
gcc -c cplusplus.cc
gfortran -o program-name fortran.o cplusplus.o
like image 45
Mike Dinsdale Avatar answered Jan 24 '23 14:01

Mike Dinsdale