2

Passing a fixed 2D array between C++ and Fortran works fine, however not so with the program I have written to pass a 2D dynamic array from C++ to Fortran.

C++ side

extern "C" {void array2d_(double **, int *, int *); }
using namespace std;
int main()
{
double **array;
int nx=3;
int ny=2;
int i,j;
cout << "Passing dynamic array from C to Fortran\n";
array = (double **) malloc(nx * sizeof(double *));
if(array == NULL)
        {
        fprintf(stderr, "out of memory\n");
        exit;
        }
for(i = 0; i < nx; i++)
        {
        array[i] = (double *) malloc(ny * sizeof(double));
        if(array[i] == NULL)
            {
            fprintf(stderr, "out of memory\n");
            exit;
            }
        }
for(i = 0; i < nx; i++)
            {
            for(j = 0; j < ny; j++)
                {
                array[i][j]=i+j+i*(2+j)+4;  //random initialisation
                cout << "array[" << i << "][" << j << "]=" << array[i][j] << " ";
                }
            cout << endl;
            }

array2d_(array, &nx, &ny);

for(i = 0; i < nx; i++)
        free(array[i]);
    free(array);
return 0;
}

The fortran side

subroutine array2d(arr,nx_C,ny_C) bind(C,name="array2d_")
use  iso_c_binding
implicit none
integer (C_INT), intent(IN) :: nx_C,ny_C          !array sizes from C
real (C_DOUBLE), intent(INOUT) :: arr(ny_C,nx_C)
integer :: k,l
print *, "This is in Fortran routine..."
do k = 1,ny_C
do l=1,nx_C
 print *, "arr(",k,",",l,") = ", arr(k,l)
end do
end do
end subroutine array2d

The output in C++ is

 array[0][0]=4 array[0][1]=5 
 array[1][0]=7 array[1][1]=9 
 array[2][0]=10 array[2][1]=13 

While in Fortran the output is

 arr(           1 ,           1 ) =    1.7994937190948764E-305
 arr(           1 ,           2 ) =    7.1027035167764720E-251
 arr(           1 ,           3 ) =    9.8813129168249309E-324
 arr(           2 ,           1 ) =    5.4809152658772852E-317
 arr(           2 ,           2 ) =    1.5475240269406953E-314
 arr(           2 ,           3 ) =    0.0000000000000000  

So somehow the values are not passed correctly.

Stam
  • 67
  • 8

2 Answers2

5

The main reason is that your C array is a jagged array, it is an array of pointers to separate 1D arrays while in Fortran you declare ther argument to be a contiguous 2D array. You must use the same in both parts, preferably use a contiguous array in C too.

Just malloc one big nx*ny buffer and set the pointers to the rows instead of alloacating them. You can see an example in https://stackoverflow.com/a/5901671/721644

Community
  • 1
  • 1
  • Sorry, are you saying to use a `double*` (with size `nx*ny`) instead of a `double**`? I am not sure I am following your suggestion. Do you mean something like `double * array; array = (double*)malloc(nx*ny*sizeof(double))` – Stam Feb 17 '15 at 14:48
  • See the linked example. You need a `double**`, but you have to set it up differently. The array must be contiguous. – Vladimir F Героям слава Feb 17 '15 at 14:55
  • I substituted all initialisation sections with the following `double *data = (double *)malloc(nx*ny*sizeof(double)); array= (double **)malloc(nx*sizeof(double*)); for (int i=0; i – Stam Feb 17 '15 at 15:31
  • Yes, you must ensure you are passing pointer to the data, not a pointer to a pointer. You need to pass `data` or `array[0]` or `&(array[0][0])` and the function must take `double *`. – Vladimir F Героям слава Feb 17 '15 at 16:43
  • So are you saying that in Fortran I can only pass the array as 1D from C++ in order for it to remain contiguous? – Stam Feb 17 '15 at 18:21
  • I am saying you must pass the address of some contiguous piece of memory. What that means for C++ I leave on you. I am not a C++ expert. Note you are basically doing C and not true modern C++. – Vladimir F Героям слава Feb 17 '15 at 19:35
  • I just find it surprising that I can pass a (partially-fixed) 2D array like this `extern "C" {void array2d(int[][2]); }` where 2 is an arbitrary number, but I cannot do the same for a dynamic array. So it appears that the only solution is to pass it as a 1D array to Fortran, which can then reconstruct it as 2D (by using the rows and columns number input). Would be nice to have something in iso_c_binding to facilitate this! – Stam Feb 17 '15 at 19:48
  • The proboem is that C (you are not really using full C++ power) does not have real 2D dynamic arrays, but only arrays of pointers. `iso_c-binding` can't do anything with that, it is a defficiency of C. – Vladimir F Героям слава Feb 17 '15 at 19:51
  • Just forgot to confirm that your suggestion works - I am passing the array as 1D but with the row and column numbers as parameters and then I reconstruct it in Fortran. Thanks! – Stam Mar 06 '15 at 07:56
  • Also, it is possible to pass a dynamic 2D array this way in C99. But not in C++. – Vladimir F Героям слава Jan 17 '22 at 09:35
0

I recently found some success using C_F_POINTER while working on something that needed to maintain the array shape in Fotran (couldn't just be 1D):

Since you are passing pointers to arrays of pointers to values (for 2D) when passing dynamically allocated arrays from C++, you can use the C_F_POINTER() method for each c_ptr to help you fill in arrays on the Fortran side.

In your Fortran subroutine, you set the variable containing your array as a 1D array of your rows. This contains an array of pointers which each point to 1D arrays of pointers that will fill in your rows with your columns. On the Fortran side, you'll need a 1D array of Fortran pointers and a 2D array that will be your actual values.

C++:

double** arrc = new double*[x]
for (i=0;i<x;i++)
    arrc[i] = new double*[y] 

some_fortran_call(arrc)

Fortran:

subroutine some_fortran_call(arrc) bind(c, name='some_fortran_call')
use iso_c_binding
type(c_ptr), intent(inout):: arrc(x)
real*8, pointer:: arrp(:)
real*8, dimension(:,:), allocatable:: arrf
allocate(arrf(x,y))

do i=1,x
    call C_F_POINTER(arrc(i), arrp, [y]) ----> Each pointer in arrc points to a 1D array with y values
    do j=1,y
        arrf(i,j) = arrp(j) ---------> Fills in ith row with y amount of values (will do this for all x rows)
    end do
end do

You still need to allocate new memory for the Fortran array but this at least saves the memory needed to allocate a new 1D array in C++.