1

I have a fortran subroutine like this which expects a 3d array and prints the size along each dimension:

subroutine printarray(arr)
real*8 :: arr(:,:,:)

print*, "Dimensions of arr -> ", size(arr(:,1,1)), size(arr(1,:,1)), size(arr(1,1,:))

end subroutine printarray

Now I want to pass a 3d array from a C program to this subroutine.

I attempted it in many ways, none of them worked.

Attempt 1:

#include <stdio.h>
#include <stdlib.h>

void printarray_(double *);

int main(int argc, char *argv[])
{
  double *ap;

  ap = (double *) malloc(sizeof(double) * 10 * 10 * 2);
  printarray_(ap);

  if(ap != NULL)
    free(ap);
  return 0;
}

Output is : 1 1 1

One issue here is I haven't explicitly specified the size of each dimension in my C code itself, so I cannot expect Fortran to figure it out. So I tried it differently:

Attempt 2:

#include <stdio.h>
#include <stdlib.h>

void printarray_(double ***);

int main(int argc, char *argv[])
{
  int i;
  int j;
  double ***a;
  a = (double ***) malloc(sizeof(double **) * 200);
  for(i = 0;i < 10;i++)
    {
      a[i] = (double **) malloc(sizeof(double *) * 10);
      for(j = 0;j < 10;j++)
      {
        a[i][j] = (double *) malloc(sizeof(double) * 2);
      }
    }

  printarray_(a);

  for(i = 0; i < 10;i++)
    {
      for(j = 0;j < 10;j++)
      {
        if(a[i][j] != NULL)
          free(a[i][j]);
      }
      if(a[i] != NULL)
        free(a[i]);
    }
  if(a != NULL)
    free(a);
  return 0;
}

Here, the output is : 289 289 1

What is the correct way of passing multidimensional arrays to Fortran subroutines from C programs, such that the size function in fortran picks up correct size of each dimension?

Alex K
  • 8,269
  • 9
  • 39
  • 57
Sachin
  • 33
  • 4
  • The first approach is the correct approach. However, from what I can recall from a long time ago, you'll have to pass the dimensions of the array also to the FORTRAN subroutine. – R Sahu Mar 11 '15 at 05:28
  • Thank you @RSahu. You are right, passing dimensions of the array explicitly would be better. The issue is that, Im trying to use a Fortran library which has defined the subroutine in this way (without taking dimensions) and Im looking for ways to do it without modifying the library. – Sachin Mar 11 '15 at 05:39
  • 1
    The other option is to create a wrapper FORTRAN subroutine which accepts the dimensions from C and then call `printarray` from the wrapper subroutine. – R Sahu Mar 11 '15 at 05:43
  • @RSahu got it. Do a C array to FORTRAN array conversion in the wrapper. – user3528438 Mar 11 '15 at 05:47
  • Yes, that is possible. Thank you so much @RSahu. – Sachin Mar 11 '15 at 05:51

2 Answers2

1

You just can not do it in C, because arrays in C decay to pointers during a function call.

There are a few ways to do it: Function parameter as array with declared size

But best and most common practice is still to pass in dimensions as separate inputs, and also pass in the address of of the beginning element and let subroutine figure out the rest itself.

And your second attempt is dangerous because dimensions are not contiguous but your function might expect/assume it to be so.

Community
  • 1
  • 1
user3528438
  • 2,737
  • 2
  • 23
  • 42
  • I cannot make function parameter as array with declared size as the size of the array is known only in run-time. – Sachin Mar 11 '15 at 05:42
  • @Sachin Then the only choice is explicitly pass size to your subroutine. – user3528438 Mar 11 '15 at 05:44
  • Thank you. You just confirmed my worst fear. And thanks for pointing out the danger in the second attempt, it didn't cross my mind while writing. – Sachin Mar 11 '15 at 05:45
  • The subroutine I want to use this on is part of a library and modifying the library code itself is my last option. Seems like I just have that one option. – Sachin Mar 11 '15 at 05:48
1

If your Fortran compiler supports ISO/IEC TS 29113:2012 , you can use the ISO_Fortran_binding.h header for interoperability of Fortran arrays with C. I am not sure which compilers support the TS; unfortunately, gfortran does not do so, in this respect. So, assumed-shape arrays like arr(:,:,:) might not be an option for you.

Failing that, you should use Fortran's Iso C binding feature.

Note that Fortran arrays consist of consecutive storage locations. For 1D-arrays, this is not an issue for C interop. For arrays with two or more dimensions, this creates problems because the normal C way of accessing arrays through pointers to pointers to ... has no good equivalent in Fortran (and is inefficient anyway).

The closest you can get in C to Fortran arrays is through the use of variable-length arrays. Unfortunately, these have been relegated to optional in the last version of the C standard, so C support for multidimensional arrays has fallen back behind Fortran 66 again. However, I am assuming that you have a compiler which supports VLAs (gcc does) and you do not hit the limitations that sometimes plague the implementaions that, for example, VLAs are only allocated on the stack and that the stack is too small for your problem size...

So, you should be able to declare

  int m,n,k;
  // Obtain a value for m,n,k
  {
     double arr[10][10][2];
     // Do something with it
     printarray(a,k,m,n)  // Fortran arrays have the opposite ordering of indices versus C
  }

... and have on the Fortran side

subroutine printarray(arr,k,m,n) bind(C,name="printarray")
  use iso_c_binding
  integer(c_int), value :: k, m, n
  real(c_double), dimension(k,m,n) :: arr
Community
  • 1
  • 1
  • All is correct, but why VLAs? Just allocate one contiguous buffer using malloc and then set an array of pointers to the rows of the buffer. – Vladimir F Героям слава Mar 11 '15 at 16:30
  • The question is how to deal with the multidimensional array on the C side. Here, VLAs offer the most ideomatic choice. Of course, you can just use indexing into the array, the same way that Fortran does under the hood, or build up a pointer forest to point into the array. Either way is rather inelegent. –  Mar 11 '15 at 17:34