Community
cancel
Showing results for 
Search instead for 
Did you mean: 
ereisch
Novice
183 Views

C interoperability with assumed-size arrays

Jump to solution

I read that Fortran assumed-shape arrays are not interoperable with C, but what about assumed-size arrays?

The BLAS library defines a subroutine CGEMM(TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC), where:

COMPLEX A(LDA, *), B(LDB, *), C(LDC, *)

Is this subroutine callable by C?  If so, are there any hidden arguments associated with the assumed-size arrays?

0 Kudos

Accepted Solutions
Steve_Lionel
Black Belt Retired Employee
177 Views

No hidden arguments - and there aren't in Fortran either with assumed-size arrays. 

Screenshot 2021-02-02 165154.png

Interestingly, you can describe an assumed-size array in a C descriptor, but it's unclear to me how you would get Fortran to generate such a thing. I guess the idea is that a C routine could create it and the -1 last extent could be detected in your Fortran code. I have to give this some more thought...

 

View solution in original post

8 Replies
Steve_Lionel
Black Belt Retired Employee
178 Views

No hidden arguments - and there aren't in Fortran either with assumed-size arrays. 

Screenshot 2021-02-02 165154.png

Interestingly, you can describe an assumed-size array in a C descriptor, but it's unclear to me how you would get Fortran to generate such a thing. I guess the idea is that a C routine could create it and the -1 last extent could be detected in your Fortran code. I have to give this some more thought...

 

View solution in original post

ereisch
Novice
156 Views
Hmm....do I get a badge for stumping Dr Fortran?

I understand they're interoperable, and the column-vs-row order implications. My question pertains more to how the dimension of the final rank would get across the interface. How does it happen with Fortran-calling-Fortran? If there isn't a hidden argument, is there a descriptor that's used (and publicly-defined) that I could utilize? Or is it just passed a reference to the first element? If so, how does the compiler know the memory stride of the second-to-last dimension?
FortranFan
Honored Contributor I
140 Views

@ereisch ,

Re:  "My question pertains more to how the dimension of the final rank would get across the interface. How does it happen with Fortran-calling-Fortran?" - forever it's worth, I'll contend there is NOT a built-in mechanism. 

What one finds is the same drawbacks since per-Fortran 90 days with assumed-size arrays i.e., the period prior to "explicit interfaces" persist in such scenarios.  That is, the caller and callee need a "handshake" of some sort to ensure the dummy array argument(s) is(are) consumed in a manner the upper array bound(s) is(are) not exceeded.  It's usually achieved via an explicit argument in the subprogram declaration rather than a hidden one that a compiler might provide.

Thus of course the proper way forward is refactored code or new code that makes use of assumed-shape arrays, etc.   In this context, your statement, "I read that Fortran assumed-shape arrays are not interoperable with C" refers to Fortran 2003 and 2008.  That is not accurate with current standard Fortran 2018.

 

ereisch
Novice
131 Views

I retract my question about the compiler determining the stride size of the last dimension; I got wrapped around the axle between C and Fortran and forgot that Fortran is column-major, so the last dimension would only need to be known at compile time to enforce bounds checking; otherwise it wouldn't care.

I think I got the info I needed though; everything goes across as REFERENCE.

Thanks

FortranFan
Honored Contributor I
113 Views
.. It's usually achieved via an explicit argument in the subprogram declaration rather than a hidden one that a compiler might provide. ..

 

This post is mostly for other readers who may prefer to see an actual example of how things work in terms of interoperability of modern Fortran with C.

As I mentioned earlier, when it comes to assumed-size arrays, their use in Fortran has also been "usually achieved via an explicit argument in the subprogram declaration"

One can see this with subprogram CGEMM from BLAS (LAPACK) library where a dummy argument 'A" is declared as 'COMPLEX, dimension(LDA,*) A' but note integer dummies K and M too are part of the argument list and the LAPACK documentation states:

A is COMPLEX array, dimension ( LDA, ka ), where ka is
           k  when  TRANSA = 'N' or 'n',  and is  m  otherwise.
           Before entry with  TRANSA = 'N' or 'n',  the leading  m by k
           part of the array  A  must contain the matrix  A,  otherwise
           the leading  k by m  part of the array  A  must contain  the
           matrix A.

Thus the needed info in terms of the upper bound of A is present interspersed among the argument list.

Now, note the Fortran standard mentions in NOTE 3 in Section 18.3.5 Interoperability of array variables,

For example, a Fortran array declared as
   INTEGER(C_INT) :: A(18, 3:7, *)
is interoperable with a C array declared as
   int b[][5][18];

 

With this in mind, a Fortran subprogram with an assumed-size array can be made to interoperate with C and for safety, an explicit argument to handle the array bounds is advised - similar to what one can see with CGEMM above.  So say the Fortran code is written as follows with a subroutine Fsub.

module m
   use, intrinsic :: iso_c_binding, only : c_int, c_size_t
   enum, bind(C)
      enumerator :: N = 3
   end enum
contains
   subroutine Fsub( x, m ) bind(C, name="Fsub")
      integer(c_int), intent(inout)        :: x(N,*)
      integer(c_size_t), intent(in), value :: m
      integer :: i
      do i = 1, m
         x(1, i) = 1
         x(2, i) = 2
         x(3, i) = 3
      end do
      return
   end subroutine
end module

Note Fsub may be a wrapper to some legacy code in some library.

Now say the C caller is like so:

#include <stdio.h>

enum N { N = 3 };
// Fortran function prototype
void Fsub( int[][N], size_t );

int main(void)
{
   enum M { M = 2 };

   int x[M][N];
   for (int i; i<2;i++) {
      for (int j; j<3; j++ ) x[i][j] = 0;
   }

   Fsub( x, (size_t)M );
   printf("Print from C main: following Fortran subroutine invocation, x = \n");
   for (int i = 0; i<M; i++) {
      for (int j = 0; j<N; j++ ) printf("%d ", x[i][j]);
   }
   printf("\n");
   return 0;
}

 

Using Intel Fortran, the program works like so:

C:\Temp>ifort /c /standard-semantics /warn:all /stand:f18 f.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.1.2 Build 20201208_000000
Copyright (C) 1985-2020 Intel Corporation.  All rights reserved.


C:\Temp>cl /c /EHsc /W3 c.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.26.28806 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

c.c

C:\Temp>link c.obj f.obj /subsystem:console /out:p.exe
Microsoft (R) Incremental Linker Version 14.26.28806.0
Copyright (C) Microsoft Corporation.  All rights reserved.


C:\Temp>p.exe
Print from C main: following Fortran subroutine invocation, x =
1 2 3 1 2 3

C:\Temp>

 

 

 

FortranFan
Honored Contributor I
108 Views

A typo in the C code in the prior thread: it should be

   for (int i=0; i<2;i++) {
      for (int j=0; j<3; j++ ) x[i][j] = 0;
   }

 

Steve_Lionel
Black Belt Retired Employee
99 Views

Assumed-size arrays depend on the called routine knowing, somehow, the array extent. The standard says the extent is from the first element in the passed argument to the last, but no information is transmitted to help with that. (There are some compilers that can optionally pass such info for diagnostic purposes.)

When you need to pass the size, prior to Fortran 90's assumed-shape arrays, one could use an "adjustable array", where the bounds are either explicitly passed in as separate arguments or come from COMMON. (As of Fortran 90 they could also come from a module or an uplevel scope.) Modern Fortran has assumed-shape arrays ,( : ) bounds, to pass all the necessary shape information.

 
Steve_Lionel
Black Belt Retired Employee
72 Views

I finally figured this out...  An assumed-size array can be passed to a procedure where the corresponding dummy argument is assumed-rank (a new F2018 concept). A dummy argument in an interoperable procedure can be assumed-rank. So if you pass an assumed-size array to such a dummy argument, a C descriptor is created. Here is an example:

module testmod
    implicit none
    contains
    subroutine sub2 (array) bind(C)
    integer, dimension(..), intent(in) :: array
    select rank (array)
      rank(*)
        print *, "Assumed size!"
      rank default
        print *, "Not assumed size"
    end select
    end subroutine sub2
end module testmod
    
program test
use testmod
implicit none
integer :: a(10)
call sub1(a)
    contains
    subroutine sub1(a)
    integer, dimension(*) :: a
    call sub2(a)
    end subroutine sub1
end program test