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,930 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
John6
Beginner
1,172 Views

As I noted in post #12, I ended up using an array of derived type with a pointer component: https://software.intel.com/en-us/forums/topic/280765. This technique worked very well, the passing of data between C++ and Fortran was seamless, and I do not think any copies were being made.

0 Kudos
qi_d_
Beginner
1,172 Views

I am getting more confused on this. When I run #10 code with Intel compiler 2016 in Windows 10, I get an output as shown in the attached output-win10.png, where the addresses are different (seems the data is copied on the Fortran side). When the same code is run on a different computer with intel 2016 installed onto Windows 7, the output (output-win7.png) looks good (same address, no copy is made). If #10 is doing the correct thing (without copying data in Fortran), it will be what I am in favor of as it looks much simpler. Any suggestions?

 

0 Kudos
JVanB
Valued Contributor II
1,172 Views

The printf() and PRINT statements are both wrong. printf() is being told to print out an address with %d format, so it only prints out the low 32 bits of the address. I don't know C all that well, but I think maybe %zd would print out all bits. Also, I don't think it's allowed to print out a type(C_PTR) with list-directed formatting, so I think you might expect more reasonable results if you printed out TRANSFER(add_foo_i,0_C_INTPTR_T). Careful examination of your outputs shows that the C printf() statements are printing out the low 32 bits of the full address seen in your Fortran outputs. I usually prefer to print out the C pointers with %p format in C and z0 format in Fortran because I am just more used to seeing these things in hex.

 

0 Kudos
John6
Beginner
1,172 Views

I ended up with something along the lines of the following code.
Note: The following is just a skeleton, but the technique worked perfectly for my real-world application.

// C++
//Declarations:
// C++ structure
extern "C" struct MyStructureC { int iData; };
// C++ Container for MyStructureC pointers
extern "C" struct MyContainerC { MyStructureC* pData; };
// Fortran subroutine declaration
extern "C" void FORTLINK MyFortranSub (MyArray* pMyArray);

// In local C++ file:
// Declare local array
vector<MyContainerC> MyCArrayVariable;
  
// Fill up the C++ array with pointers
MyCArrayVariable.resize(1) // Size of one is simply for example

// Load in your C pointer(s) here, loop as needed
MyCArrayVariable[0].pData = [fill in a pointer to some desired MyStructureC variable here, i.e., *pMyStructureC];
  
// Now call the Fortran subroutine
MyFortranSub (&MyCArrayVariable[0]);

--------------------------------------------------------------
--------------------------------------------------------------

Now for the Fortran side:

! Declarations:
Type MyStructureFortran
  Sequence
  Integer :: iData
End Type MyStructureFortran

Type MyContainerFortran
  Sequence
  Type (MyStructureFortran), Pointer :: pStructure
End Type MyContainerFortran

! Fortran Subroutine Code called from C++:

Subroutine MyFortranSub (var)
  Type (MyContainerFortran), Dimension(*), Target :: var
  Integer :: var2

  ! Example Usage
  Do i = 1, n
    var2 = var(i)%pStructure%iData
  End Do

  Return
End Subroutine MyFortranSub
0 Kudos
FortranFan
Honored Contributor III
1,172 Views

qi d. wrote:

.. When I run #10 code with Intel compiler 2016 in Windows 10, .. the addresses are different (seems the data is copied on the Fortran side). When the same code is run on .. Windows 7, the output .. looks good (same address, no copy is made). ..

@qi d.,

Perhaps you want to open up another thread (you can reference this thread in it) to discuss the issue where you are seeing different behavior on Windows 10 versus 7 with the same code and (apparently) the same compiler.  This way the issue will get noticed better and someone might be able to help you out.  You may want to post the relevant files (actual code, Visual Studio project files if you are using VS, compiler output logs, etc.), not just screen images and comments, to have better success in resolving the issue.  In the new thread, you may also want to follow up on comments by Steve Lionel (Intel) as to how the " the code in #10 is faulty" is related to different behavior with the same Intel compiler on Windows 10 versus Windows 7.  And the same with RO on how the comment in Message #24 of "The printf() and PRINT statements are both wrong" is pertinent to Windows 10.

0 Kudos
JVanB
Valued Contributor II
1,172 Views

@F**2,

The code in Quote #10 is faulty for the 2 reasons I pointed out. You are just going to have to learn to live with that reality.

Recall that in C, printf() is a function, not a statement like PRINT is in Fortran, thus printf() doesn't really have any context to know the effective KINDs of its actual arguments, so it must muddle through as best as possible from the information you provide in the format string. Your format for the address was %d, so what printf() is going to do is to look at the next argument and convert its low 32 bits to decimal and print that to standard output. In Window 7 it came out as 00000000001CFE08 and the low 32 bits were 001CFE08 which prints as 1900040. In Windows 10, the address was 000000C55837F828, the low 32 bits was 5837F828, so it printed out 1480063016. There may be some security feature (ASLR) active in the Windows 10 context but not in Windows 7 that makes the difference.

Fortran can use the context of the PRINT statement to figure out the size of the next data item to be printed. gfortran, with -std=f2003 rejects your Fortran code with

sub.f90:30:57:

          print *, " address of foo%(",i,") = ", add_foo_i
                                                         1
Error: GNU Extension: Data transfer element at (1) cannot have PRIVATE component
s

But it will accept transfer(add_foo_i,0_C_INTPTR_T), provided that your USE statement is expanded appropriately.

In C, you need a format that prints out 32 bits in a 32-bit context and 64 bits in a 64-bit context. It would be nice if %ld or %lld would be the right thing, but C doesn't have a nice KIND system like Fortran does, so neither of these is guaranteed to be right. But since C_INTPTR_T seems to be the same as C_SIZE_T on Windows at least, %zd should work. Just checking on gcc, it doesn't, however, so you need some sort of preprocessor noise to figure out whether you need %ld or %lld. Of course if you printed out in %p and z0 formats there would be no problems.

Print from C main:
 address of foo[0] = 000000000023FDF0
 address of foo[1] = 000000000023FDF4
 address of foo[2] = 000000000023FDF8
Print from C function:
 foo[0].i = 0
 foo[1].i = 1
 foo[2].i = 2
 address of foo[0] = 000000000023FDF0
 address of foo[1] = 000000000023FDF4
 address of foo[2] = 000000000023FDF8
  Print from Fortran subroutine:
  foo%(           1 ) =            0
  foo%(           2 ) =            1
  foo%(           3 ) =            2
  address of foo%(         1) = 23FDF0
  address of foo%(         2) = 23FDF4
  address of foo%(         3) = 23FDF8

Edit: Oh yes, I forgot to mention that your example passed a 1-d array and not a 2-d array. Passing a 2-d array between C and Fortran seems to me to be a problem in that at least older versions of C didn't have anything really like a 2-d array, just an array of pointers, each pointing effectively to a row of the array. How do you get C and Fortran to agree that what gets passed from one to the other is the address of a contiguous block of memory that contains the whole 2-d array, not the address to an array of pointers to rows?

 

0 Kudos
JVanB
Valued Contributor II
1,172 Views

I finally made the counterexample to Quote #10 work on gcc/gfortran. Compile them with:

C:\>gfortran -c sub0.f90

C:\>gcc main0.c sub0.o -omain0 -lgfortran -Wl,--dynamicbase,
--export-all-symbols

Now the addresses are different every time:

C:\>main0
Print from C main:
 address of foo[0] = 17563104
 address of foo[1] = 17563108
 address of foo[2] = 17563112
Print from C function:
 foo[0].i = 0
 foo[1].i = 1
 foo[2].i = 2
 address of foo[0] = 17563104
 address of foo[1] = 17563108
 address of foo[2] = 17563112
  Print from Fortran subroutine:
  foo%(           1 ) =            0
  foo%(           2 ) =            1
  foo%(           3 ) =            2
  address of foo%(           1 ) =              17563104
  address of foo%(           2 ) =              17563108
  address of foo%(           3 ) =              17563112

C:\>main0
Print from C main:
 address of foo[0] = 14089040
 address of foo[1] = 14089044
 address of foo[2] = 14089048
Print from C function:
 foo[0].i = 0
 foo[1].i = 1
 foo[2].i = 2
 address of foo[0] = 14089040
 address of foo[1] = 14089044
 address of foo[2] = 14089048
  Print from Fortran subroutine:
  foo%(           1 ) =            0
  foo%(           2 ) =            1
  foo%(           3 ) =            2
  address of foo%(           1 ) =              14089040
  address of foo%(           2 ) =              14089044
  address of foo%(           3 ) =              14089048

But unfortunately the addresses are still 32 bit size so the bug in Quote #10 is not evident. We can fix this with:

C:\>editbin /HIGHENTROPYVA main0.exe
Microsoft (R) COFF/PE Editor Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.

Now the addresses really are 64 bits and the bug shows itself:

C:\>main0
Print from C main:
 address of foo[0] = -361826704
 address of foo[1] = -361826700
 address of foo[2] = -361826696
Print from C function:
 foo[0].i = 0
 foo[1].i = 1
 foo[2].i = 2
 address of foo[0] = -361826704
 address of foo[1] = -361826700
 address of foo[2] = -361826696
  Print from Fortran subroutine:
  foo%(           1 ) =            0
  foo%(           2 ) =            1
  foo%(           3 ) =            2
  address of foo%(           1 ) =          386185229936
  address of foo%(           2 ) =          386185229940
  address of foo%(           3 ) =          386185229944

So the difference may be that on Windows 10 /HIGHENTROPYVA is the default.

 

0 Kudos
JVanB
Valued Contributor II
1,172 Views

Seems that C99 VLAs pass 2-d arrays smoothly.

module m

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

   implicit none

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

contains

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

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

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

      !..
      print *, " Print from Fortran subroutine:"
      do i = 1, size_i
         do j = 1, size_j
            print '(1x,a,i0,a,i0,a,i0.2)', " foo%(",j,",",i,") = ", foo(j,i)%i
         end do
      end do
      do i = 1, size_i
         do j = 1, size_j
            add_foo_i = c_loc(foo(j,i))
            print '(1x,a,i0,a,i0,a,z0)', " address of foo%(",j,",",i,") = ", &
               transfer(add_foo_i,0_C_INTPTR_T)
         end do
      end do

      !..
      return

   end subroutine somesub

end module m


#include <stdio.h>
#include <malloc.h>

typedef struct {
   int i;
} foo_c;

void C_printf(int M, int N, foo_c
  • ); void F_printf(int M, int N, foo_c
  • ); int main () {    int M = 3, N = 2;    int i, j;    foo_c (*foo);    foo = malloc(sizeof(foo_c));    for (i = 0; i < M; i++)    {       for(j = 0; j < N; j++)       {          (*foo).i = 10*i+j;       }    }    printf("Print from C main:\n");    for (i = 0; i < M; i++)    {       for(j = 0; j < N; j++)       {          printf(" address of foo[%d][%d] = %p\n", i, j, &((*foo)));       }    }    C_printf(M, N, *foo);    F_printf(M, N, *foo);    return 0; } void C_printf(int M, int N, foo_c foo) {    int i, j;    printf("Print from C function:\n");    for (i = 0; i < M; i++)    {       for(j = 0; j < N; j++)       {          printf(" foo[%d][%d].i = %2.2d\n", i, j, foo.i);       }    }    for (i = 0; i < M; i++)    {       for(j = 0; j < N; j++)       {          printf(" address of foo[%d][%d] = %p\n", i, j, &foo);       }    } }
  • C:\>gfortran sub1.f90 -c
    
    C:\>gcc main1.c -omain1 sub1.o -lgfortran -Wl,--dynamicbase,
    --export-all-symbols -Wl,--high-entropy-va
    
    C:\>main1
    Print from C main:
     address of foo[0][0] = 00000017B1406AE0
     address of foo[0][1] = 00000017B1406AE4
     address of foo[1][0] = 00000017B1406AE8
     address of foo[1][1] = 00000017B1406AEC
     address of foo[2][0] = 00000017B1406AF0
     address of foo[2][1] = 00000017B1406AF4
    Print from C function:
     foo[0][0].i = 00
     foo[0][1].i = 01
     foo[1][0].i = 10
     foo[1][1].i = 11
     foo[2][0].i = 20
     foo[2][1].i = 21
     address of foo[0][0] = 00000017B1406AE0
     address of foo[0][1] = 00000017B1406AE4
     address of foo[1][0] = 00000017B1406AE8
     address of foo[1][1] = 00000017B1406AEC
     address of foo[2][0] = 00000017B1406AF0
     address of foo[2][1] = 00000017B1406AF4
      Print from Fortran subroutine:
      foo%(1,1) = 00
      foo%(2,1) = 01
      foo%(1,2) = 10
      foo%(2,2) = 11
      foo%(1,3) = 20
      foo%(2,3) = 21
      address of foo%(1,1) = 17B1406AE0
      address of foo%(2,1) = 17B1406AE4
      address of foo%(1,2) = 17B1406AE8
      address of foo%(2,2) = 17B1406AEC
      address of foo%(1,3) = 17B1406AF0
      address of foo%(2,3) = 17B1406AF4
    

     

    0 Kudos
    Reply