Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29277 Discussions

Pass Array of Pointers from a C++ function into a Fortran subroutine

John6
Beginner
4,920 Views

I am attempting to pass a C++ array of pointers into a Fortran subroutine.

The C++ array is of a structure, which exactly conforms to a Fortran derived type.

So in C++ I have pArray* c_arr[DIM1][DIM2];

Then I call a Fortran sub, passing in the C++ array of pointers like fortsub(c_arr[0][0])

In Fortran I have Subroutine fortsub (fort_arr) where I need fort_arr to be a container for the array of pointers.

I currently do not know how to make this work, I have something like:

Type (type1), Dimension(2,*), Target :: fort_arr where type1 is the same type as pArray.

Basically, my problem is I don't know how to pass a double dimensioned array of C++ structure pointers into a Fortran subroutine.

I know that he concept of "array of pointers" does not exist in Fortran and the way you have to do it is have an array of derived type with a pointer component, but I cannot seem to get the data passing on the argument list correct.

Anyone have some advice, perhaps with a specific example? I would appreciate it greatly. Thanks.

0 Kudos
28 Replies
TimP
Honored Contributor III
3,752 Views

Those are "integer pointers," or "Cray pointers," which don't operate with target, until you use c_f_pointer to make Fortran pointers of them. Also, a C ** isn't interoperable with Fortran arrays.  I suppose you would need to "unlock" access individually to the 1D C arrays which would become interoperable arrays of integer pointers.

I'm not confident that I know what you mean about your C definitions.  What you show doesn't look like a struct but may be a ***.  I am more comfortable with things which ought to be directly interoperable.

0 Kudos
John6
Beginner
3,752 Views

I probably didn't explain the problem well enough with my example, and I will try to elaborate a bit more on Monday.

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,752 Views

To make life easier (you may be doing this already) is on the C side, allocate the storage for the 2D array as one allocation (1D), Then independently allocate or use an additional 1D array of C pointers (# pointers = # rows), and fill in your pointers for C to use. Then on the call to Fortran, pass in the extents (#rows, #cols) and the pointer to the data blob (aka pointer[0] same as &(array[0][0]) .

On the Fortran side you can use the information to construct an array descriptor without copying the data. Caution, the indices are transposed between Fortran and C. Following data stored in memory in order of value depicted:

0 1 2 3
4 5 6 7

C has cell containing 6 as A[1][2]
Fortran has cell containing 6 as A(2,3) ! using 1-based indexing
You can construct an array descriptor that is 0-base if you desire (but cannot transpose the indexing)

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor III
3,752 Views

Note Fortran offers standard interoperability with C, not C++ so you will have to apply extern "C" linkage on your C++ side if you want to use standard approach.

Assuming you follow Jim's advice and go with 1D array of pointers to simplify the matter, here's an approach you can follow shown using C code:

#include <stdio.h>

void C_printf(int** iptr, int M);
void F_printf(int** iptr, int M);

int main ()
{

   const int M = 3;

   int  v[] = {1, 2, 3};
   int *ptr;
   int i;

   for (i = 0; i < M; i++)
   {
      ptr = &v; /* assign the address of integer. */
   }

   C_printf( ptr, M );
   F_printf( ptr, M );

   return 0;
}

void C_printf(int** iptr, int M)
{

   printf("Print from C function:\n");
   for (int i = 0; i < M; i++)
   {
      printf("Value of iptr[%d] = %d\n", i, *iptr);
   }

}
module m

   use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer

contains

   subroutine somesub( c_iptr, size_i ) bind(c, name="F_printf")

      !.. Argument list
      type(c_ptr), intent(in)           :: c_iptr(*)
      integer(c_int), intent(in), value :: size_i

      !.. Local variables
      integer(c_int), pointer :: f_i
      integer(c_int) :: i

      print *, " Print from Fortran subroutine:"
      do i = 1, size_i
         call c_f_pointer( c_iptr(i), f_i )
         print *, "Value of iptr[",i,"] = ", f_i
      end do

      !..
      return

   end subroutine somesub

end module m

Upon execution,

Print from C function:
Value of iptr[0] = 1
Value of iptr[1] = 2
Value of iptr[2] = 3
  Print from Fortran subroutine:
 Value of iptr[           1 ] =            1
 Value of iptr[           2 ] =            2
 Value of iptr[           3 ] =            3

 

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,752 Views

I was thinking more along the line of: (untested code)

#include <stdio.h>

void C_printf(int** iptr, int M);
void F_printf(int** iptr, int M);

int main (int argc, char* argv[])
{
  // get the unknown size of the 2D array
  int nRows = atoi(argv[1]);
  int nCols = nRows; // square array
  // allocate array as 1D
  int Aas1D* = (int*)malloc(nRow*nCols*sizeof(int));
  // allocate an array of pointers to each row
  int** A = (int**)malloc(nRows*sizeof(int*));
  // fill in the pointers to the rows
  for(int iRow=0; iRow<nCols; ++iRow)
    A[iRow] = &Aas1D[iRow * nCols];
  // The array A[iRow][iCol] is now available
  // use the array
  for(int iRow = 0; iRow < nRows; ++iRow)
    for(int iCol = 0; iCol < nCols; ++ iCol)
      A[iRow][iCol] = iRow * 1000 + iCol; // insert data

   C_printf( A, nRows );
   F_printf( &(A[0][0]), nRows );
   F_printf( A[0], nRows );
   F_printf( Aas1D, nRows );

   return 0;
}

void C_printf(int** iptr, int M)
{

   printf("Print from C function:\n");
   for (int iRow = 0; iRow < M; iRow++)
     for(int iCol = 0; iCol < M; iCol++)
     {
       printf("Value of iptr[%d][%d] = %d\n", iRow, iCol, iptr[iRow][iCol]);
     }

}

module m

   use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer

contains

   subroutine somesub( c_iptr, size_i ) bind(c, name="F_printf")

      !.. Argument list
      type(c_ptr), intent(in)           :: c_iptr
      integer(c_int), intent(in), value :: size_i

      !.. Local variables
      integer(c_int), pointer :: f_i(:,:)
      integer(c_int) :: iRow, iCol
      call c_f_pointer( c_iptr(i), f_i, [size_i, size_i] )
      print *, " Print from Fortran subroutine:"
      do iRow = 1, size_i
        do iCol = 1, size_i
          print *, "Value of f_i(",iCol",",iRow,") = ", f_i(iCol, iRow)
        end do
      end do

      !..
      return

   end subroutine somesub

end module m

Jim Dempsey

0 Kudos
John6
Beginner
3,752 Views

Thank all of you for the tips, they are very much appreciated. I have something like the following, which seems to work now.
Note: Apologies, I am not too familiar with iso_C_binding, or what benefits it can bring to the table.
Most of my work has been done manipulating programs written in Fortran77/90, and I have added heavy usage of derived types and pointers, but haven't used modules. I have had great success in calling Fortran functions from C++, and do know about the reversed rows-columns for 2D arrays between C++ and Fortran.

So, what I have is this, which seems to work OK. In general, I am trying to avoid copying the data structures between C and Fortran.
Simply passing in the 2D array which is loaded with structure pointers will allow the Fortran subroutine to modify the C structures accordingly.
Also, will passing in the number of rows and columns for array bounds error-checking, just haven't done it yet.

C++ :
  extern "C" struct MyStructC {
    char s1[3];
    int i1;
    float f1;
  };

  extern "C" struct MyStructCPointerArray { MyStruct* pMyStructC; };

  MyStructCPointerArray structvar[100][2]; // Local 2D C++ array which will contain pointers to MyStructC
  for (int i=1; i<=2; ++i) { structvar[0][i-1].pMyStructC = getPointerToMyStructC(); } // Fill data with pointer(s)

  fortfunc(&structvar[0][0]);    // Call to Fortran


Fortran :

Type Declaration:
      Type MyStructFortran
        Sequence
        Character(3) :: s1
        Integer :: i1
        Real :: f1
      End Type MyStructFortran

      Type MyStructFortranPointerArray
        Sequence
        Type (MyStructFortran), Pointer :: pMyStructFortran
      End Type MyStructFortranPointerArray


Subroutine fortfunc (MyFortranVar)
  ! Argument List Variable Declarations
  Type (MyStructFortranPointerArray), Dimension(2,*), Target :: MyFortranVar

  ! Local Variable Declarations
  Integer :: i

  ! Local Pointer Declarations
  Type (MyStructFortran), Pointer :: pStruct1, pStruct2

  pStruct1 = MyFortranVar(1,1)%pMyStructFortran
  pStruct2 = MyFortranVar(2,1)%pMyStructFortran

End Subroutine fortfunc

 

0 Kudos
FortranFan
Honored Contributor III
3,747 Views

John wrote:

... In general, I am trying to avoid copying the data structures between C and Fortran.

Simply passing in the 2D array which is loaded with structure pointers will allow the Fortran subroutine to modify the C structures accordingly. ..

It seems like you just need to pass an array of structs across from C++ to Fortran as the code in your second post doesn't quite jive with your explanations about an array of pointers in C++ needing to be used in Fortran.  It's unclear why you even need an array of pointers.  Why do you seem to think you are avoiding copying when you have a statement in Fortran such as "pStruct1 = MyFortranVar(1,1)%pMyStructFortran"?

 

0 Kudos
John6
Beginner
3,747 Views

I believe there is a difference between passing the entire 2D array of structures vs. passing a 2D array of pointers to the structures.
Wouldn't a copy of the entire 2D array be made is the first situation vs. no copy in the second situation?
Or is no copy made in the first situation, in which case I wouldn't need an array of pointers at all.
 

0 Kudos
FortranFan
Honored Contributor III
3,747 Views

John wrote:

I believe there is a difference between passing the entire 2D array of structures vs. passing a 2D array of pointers to the structures.
Wouldn't a copy of the entire 2D array be made is the first situation vs. no copy in the second situation?
Or is no copy made in the first situation, in which case I wouldn't need an array of pointers at all.


No copy need be made, check it out yourself with your struct:

#include <stdio.h>

typedef struct {
   int i;
} foo_c;

void C_printf(foo_c *f, int M);
void F_printf(foo_c *f, int M);


int main ()
{

   const int M = 3;

   int i;
   foo_c foo;

   for (i = 0; i < M; i++)
   {
      foo.i = i;
   }

   printf("Print from C main:\n");
   for (int i = 0; i < M; i++)
   {
      printf(" address of foo[%d] = %d\n", i, &foo);
   }

   C_printf( foo, M );
   F_printf( foo, M );

   return 0;
}

void C_printf(foo_c *foo, int M)
{

   printf("Print from C function:\n");
   for (int i = 0; i < M; i++)
   {
      printf(" foo[%d].i = %d\n", i, foo.i);
   }
   for (int i = 0; i < M; i++)
   {
      printf(" address of foo[%d] = %d\n", i, &foo);
   }

}
module m

   use, intrinsic :: iso_c_binding, only : c_int, c_loc, c_ptr

   implicit none

   type, bind(c) :: foo_f
      integer(c_int) :: i
   end type foo_f

contains

   subroutine somesub( foo, size_i ) bind(c, name="F_printf")

      !.. Argument list
      type(foo_f), intent(inout), target :: foo(*)
      integer(c_int), intent(in), value  :: size_i

      !.. Local variables
      integer :: i
      type(c_ptr) :: add_foo_i

      !..
      print *, " Print from Fortran subroutine:"
      do i = 1, size_i
         print *, " foo%(",i,") = ", foo(i)%i
      end do
      do i = 1, size_i
         add_foo_i = c_loc(foo(i))
         print *, " address of foo%(",i,") = ", add_foo_i
      end do

      !..
      return

   end subroutine somesub

end module m

Upon execution,

Print from C main:
 address of foo[0] = 2686540
 address of foo[1] = 2686544
 address of foo[2] = 2686548
Print from C function:
 foo[0].i = 0
 foo[1].i = 1
 foo[2].i = 2
 address of foo[0] = 2686540
 address of foo[1] = 2686544
 address of foo[2] = 2686548
  Print from Fortran subroutine:
  foo%(           1 ) =            0
  foo%(           2 ) =            1
  foo%(           3 ) =            2
  address of foo%(           1 ) =      2686540
  address of foo%(           2 ) =      2686544
  address of foo%(           3 ) =      2686548


If you do plan to do more interoperation between C++ and Fortran via "extern C" linkage, do look into the facilities offered in Intel Fortran based on standard Fortran features from Fortran 2003 onward.

0 Kudos
John6
Beginner
3,747 Views

I understand what you are saying, but I cannot seem to get a 2D array to be passed in correctly.
If I declare my C array of structures as MyStructC* structvar[100][2];
then fill it with structure pointers and then pass it to Fortran like : fortfunc(structvar[0][0])

where in Fortran I have:
Subroutine fortfunc (MyFortranVar)
Type (MyStructFortran), Dimension(2,*), Target :: MyFortranVar

the first structure: MyFortranVar(1,1) is OK (meaning it contains the same data as structvar[0][0]), but MyFortranVar(1,2) is not OK (meaning it does not contain the same data as structvar[0][1].

 

0 Kudos
John6
Beginner
3,747 Views

Well, I guess the only solution is to have an array of derived type with a pointer component: https://software.intel.com/en-us/forums/topic/280765. This is what my example above is doing, and it works well enough, I suppose.

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,747 Views

>>If I declare my C array of structures as MyStructC* structvar[100][2];

Add

MyStructC* MyStruct2DArray = new MyStructC[100*2];
for(int iRow = 0; iRow < 100; ++iRow)
  for(int iCol=0; iCol < 2; ++iCol)
    structvar[iRow][iCol] = &MyStruct2DArray[iRow*2 + iCol];

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,747 Views

IOW allocate the blob of structures as one allocation (i.e. 1D array)...
Then construct your 2D array of pointers to these objects for use by C/C++.

Pass the address of the 1st element and have Fortran construct the array descriptor for the 2D array.

Be mindful that you may get your rows and columns mixed up (run test data to assure you have things right).

Jim Dempsey

0 Kudos
qi_d_
Beginner
3,747 Views

Thank you guys for this nice post.

I am new to this field, and am learning to do similar things as shown in #10 by FortranFan. When I compile his Fortran code with IVF, I obtain error #7300 The derived-type object in an input-output list cannot have the private components. After commenting lines on taking address of foo(i), the fortran code works well with integration into C++, but I cannot check the address as FortranFan did. Does anyone have any hint on how to get rid of the error? Many thanks. 

QD 

0 Kudos
mecej4
Honored Contributor III
3,747 Views

Are you using an older version of the IFort compiler? If so, which one? It builds fine with the current compiler, 16.0.1.

0 Kudos
Steven_L_Intel1
Employee
3,752 Views

Very interesting. 15.0 gives that error, 16.0 doesn't. 15.0 is correct - type C_PTR has a private component and you're not allowed to use it in an I/O list without a user-defined derived type I/O procedure. At first blush it looks as if we introduced a bug - I will look into it.

The correct way to do this would be to replace add_foo_i in the I/O list with TRANSFER(add_foo_i,0_INTPTR_T).

0 Kudos
Steven_L_Intel1
Employee
3,752 Views

Looks as if we already fixed this as part of a number of other fixes to UDDTIO. The next major release will, once again, give an error for this use.

0 Kudos
qi_d_
Beginner
3,752 Views

Sorry that I did not make it clear. I am currently using Intel Composer XE 2013 sp1 which gives me this error.

0 Kudos
Steven_L_Intel1
Employee
3,752 Views

Yes, the error message is correct - the code in #10 is faulty but the 16.0 (2016) compiler doesn't give the error. That is an aberration.

0 Kudos
qi_d_
Beginner
3,513 Views

Steve, thank you for the response. 

I upgraded my IVF to 16.0 (2016), and made a DLL by adding 

!DEC$ ATTRIBUTES DLLEXPORT :: somesub 

below Line 13 subroutine somesub( foo, size_i ) bind(c, name="F_printf") of the Fortran code in #10. 

 

After modifying Line 08 void F_printf(foo_c *f, int M); with extern "C" {} in the C code, I am able to call the Fortran subroutine somesub from intel C++ 16.0 with MSVS2012.

Unlike what is shown in #10, in my output, the address of foo%(i) is different from that of foo. It seems that copy has been made, which is not desired in my application. If I am passing an array of structs (the array dimension is well defined in C++) to Fortran, what is the proper implementation without invoking unnecessary copy as discussed by John in #7 or #9? Sticking to the example in #10 is more appreciated. 

QD 

 

 

0 Kudos
Reply