Pass Arrays from C/C++ to Fortran and Return a Calculated Array

Pass arrays from C/C++ to Fortran and return a calculated array

Before francescalus confirms it, I was going to say that from what I know that was a little bit old, the interoperability does not permit what you are trying to do with arrays.
In addition, some good habits are always critical when coding. For example using implicit none in fortran to force the declaration of all variables before they are used. The use of named constant when the language permits it, for example the 2 that you are using as array size in fortran.

Below is a modified version of your code that should do something like what you want to achieve.

//Fortran

module ConvertUnitsLib

use :: iso_c_binding ! for C/C++ interop
!real(c_double), bind(c) :: degF, degC
implicit none

public DegCtoF

contains

!
! Convert temperature degrees Celsius Fahrenheit
!
subroutine DegCtoF(degC, degF, n)&
bind(c, name = "DegCtoF")

integer, intent(in) :: n
real(c_double), intent(in), dimension(n) :: degC
real(c_double), intent(out), dimension(n) :: degF
integer :: i

do i = 1, n
degF(i) = ( degC(i) * 1.8 ) + 32
end do

end subroutine DegCtoF

// C++

#include <stdio.h>

#ifdef __cplusplus
extern"C" {
#endif
double DegCtoF(double [], double [], const int *);
#ifdef __cplusplus
}
#endif

/**********************************************************************/

int main(int argc, char *argv[])
{
const int N = 2;
printf("C/C++ and Fortran together!\n");

double DegreesC[N] = {32, 64};
double DegreesF[N];

DegCtoF(DegreesC, DegreesF, &N);
for(int i = 0; i<N; i++){
printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i] );
}

return 0;
}

How to pass array to a procedure which is passed as an argument to another procedure using Fortran

It's the assumed-shape array dummy arguments dimension(:) that require an explicit interface. This is because the compiler has to do something different for these, typically passing a descriptor. It doesn't matter what's on the calling side, it's the dummy arguments of the called procedure that matter.

For more information see my old blog post Doctor Fortran Gets Explicit - Again!

You may want to look at the language feature abstract interface, along with procedure, to make the code look a bit cleaner.

How to access (dynamically allocated) Fortran arrays in C

In my opinion it is not good practice to try to access global data in Fortran library. It can be done using COMMON blocks, but they are evil and require statically sized arrays. Generally storage association is a bad thing.

Never access the module symbols as "__bar_MOD_a" they are compiler specific and not meant to be used directly. Pass poiters using functions and subroutines.

Pass the array as a subroutine argument. You can also allocate the array in C and pass it to Fortran. What can be also done is getting a pointer to the first element of the array. It will serve es the C pointer to the array.

My solution, for simplicity without the .so, it is trivial to add it:

bar.f90

module bar
use iso_C_binding

implicit none

integer, parameter :: pa = selected_real_kind(15, 307)

real(pa), dimension(:), allocatable,target :: a
integer :: as

contains

subroutine allocArray(asize,ptr) bind(C,name="allocArray")
integer, intent(in) :: asize
type(c_ptr),intent(out) :: ptr

as = asize
allocate(a(asize))

ptr = c_loc(a(1))
end subroutine

subroutine fillArray(values) bind(C,name="fillArray")
real(pa), dimension(as), intent(in) :: values

a = values
end subroutine

subroutine printArray() bind(C,name="printArray")

write(*,*) a
end subroutine

end module

main.c

#include <dlfcn.h>
#include <stdio.h>

int main()
{
int i, k = 4;
double arr[k];
char * e;
double *a;
void allocArray(int*,double**);
void fillArray(double*);
void allocArray();

for(i = 0; i < k; i++)
arr[i] = i * 3.14;

allocArray(&k,&a);
fillArray(arr);
printArray();
for(i = 0; i < 4; i++)
printf("%f ", a[i]);
printf("\n");

return 0;
}

compile and run:

gcc -c -g main.c

gfortran -c -g -fcheck=all bar.f90

gfortran main.o bar.o

./a.out
0.0000000000000000 3.1400000000000001 6.2800000000000002 9.4199999999999999
0.000000 3.140000 6.280000 9.420000

Note: There is no reason for the returns in your Fortran subroutines, they only obscure the code.



Related Topics



Leave a reply



Submit