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

Passing 2D dynamic array from C/C++ to Fortran

Burak_C_
Beginner
1,310 Views

Hi,

I would like to pass a 2D dynamic array from C++ to a Fortran subroutine(dll) for some calculations and after that I would like to get the modified array back to my C++ program.

I have checked lots of documents but I didn't find a solution for my case. Similarly , IanH offers a solution from this forum (https://software.intel.com/en-us/forums/topic/288940) but it is not what I want to do. I don't want to construct the array inside Fortran subroutine, I would like to modify the existing one and get it back.

Also, from this link, Oracle documentation states that: "C pointers are compatible with Fortran 95 scalar pointers, but not array pointers." The C++ dynamic arrays are the 1D array of array pointers so my case seems inapplicable.

Would you offer any solution? What kind of approach should I follow?

Thanks in advance,

//C++ code

#include <iostream>
extern "C" __declspec (dllimport) void FortCDLL(int*, int*,float **,const int*, const int*);

int main ()
{
	const int row = 2;
	const int col = 3;
	int var1 = 5;
	int var2 = 6;

	// Allocate memory
  float **p2DArray = new float*[row];
  for (int i = 0; i < row; ++i)
	  p2DArray = new float[col];
      // initialization
  for(int i = 0; i < row; i++)
	for(int j = 0; j < col; j++)
		p2DArray = (i + j) * i + j;

    //Call Fortran dll
FortCDLL(&var1, &var2, array2D, &row, &col);

	return 0;
}

//Fortran Subroutine

!  FortCDLL.f90 
!
!  FUNCTIONS/SUBROUTINES exported from FortCDLL.dll:
!  FortCDLL - subroutine 
!
subroutine FortCDLL(VAR1, VAR2,C_ARR, ROW, COL) BIND(C, NAME="FortCDLL")
  USE, INTRINSIC :: ISO_C_BINDING
  IMPLICIT NONE
     
  !DEC$ ATTRIBUTES DLLEXPORT::FortCDLL

  ! Variables
  INTEGER(C_INT), INTENT(INOUT) :: VAR1, VAR2
   
  REAL(C_FLOAT), POINTER :: arr(:,:)
  INTEGER(C_INT), INTENT(IN) :: ROW, COL
 TYPE(C_PTR), INTENT(IN) :: C_ARR

 !Dummy variables
  INTEGER DummyARR(ROW,COL)
  INTEGER VEC(2)
  INTEGER :: i, j 
   
 ! Body of FortCDLL
  VEC = SHAPE(DummyARR)
  PRINT *, "Fortran DLL is called ... "
  CALL C_F_POINTER(C_ARR, arr, VEC)

  PRINT *, "### assignment ... ###"
  DO i = 1, ROW
    DO j = 1, COL
        arr (i, j) = (VAR1 + VAR2) * i + j
        PRINT *, "arr(",i,j,")= ", arr(i,j)
    END DO
  END DO

  VAR1 = VAR2 + 257;
  VAR2 = 34;

  PRINT *, "END of Fortran DLL ..." 
end subroutine FortCDLL

 

0 Kudos
2 Replies
Yu__Tao
Beginner
1,310 Views

Hi, Burak

If you want to pass a 2D dynamic array from C++ to a Fortran, you can declare the it to be an 1D dynamic array in c++. Actually Array[2][3] is the same with Array[6] in memory, c++ store the array continously row by row. Be careful in:

  1. c++/c stores array is row-major, while Fortran stores array in column-major order. So you need to do some kind of conversion.
  2. Use correct data type mapping, e.g. c++ double – fortran real(8), c++ float – fortran real(4).

I modified your example a little bit. You can see it works now.

c++:

extern "C"

{

extern void __stdcall test(int*, int*,float *,const int*, const int*);

}

int _tmain(int argc, _TCHAR* argv[])

{

const int row = 2;

const int col = 3;

int var1 = 5;

int var2 = 6;

// Allocate memory

float *p2DArray = new float [row*col];

// initialization

for(int i = 0; i < row; i++)

    for(int j = 0; j < col; j++)

        p2DArray[i*col+j] = (i + j) * i + j;

//Call Fortran dll

test(&var1, &var2, p2DArray, &row, &col);

return 0;

}

 

Fortran:

SUBROUTINE test ( VAR1, VAR2, ARR, ROW, COL )

CDEC$ ATTRIBUTES DLLEXPORT, STDCALL :: test

CDEC$ ATTRIBUTES REFERENCE ::VAR1, VAR2, ARR, ROW, COL

IMPLICIT NONE

INTEGER, INTENT(INOUT) :: VAR1, VAR2

INTEGER, INTENT(IN) :: ROW, COL

REAL(4), INTENT(INOUT) :: ARR(ROW, COL)

INTEGER :: i, j

DO i = 1, ROW

DO j = 1, COL

ARR (i, j) = (VAR1 + VAR2) * i + j

END DO

END DO

VAR1 = VAR2 + 257

VAR2 = 34

end subroutine test

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,310 Views

One option is on the C++ side is to allocate the 2D array of floats as 1D and assigned to the first pointer of pointers, then fill in the remainder pointers in the array of pointers with the proper offset. And on the Fortran side you can now create the 2D array descriptor. One call to new on C++ side, no copy on Fortran side.

The other option where C++ side uses multiple new's, and is what you are attempting to do is to define a type that is a 1D pointer to an array of floats, then define an array of these pointers

outline:

type p1D
real, pointer :: a(:)
end type p1D

type(p1D) :: arrayOfp1d(:)
...
allocate(arrayOfp1d(nRows))
// then initialize the pointers to the corrisponding locations in the C++ array of pointers
...
then

DO i = 1, ROW 
  DO j = 1, COL 
    arrayOfp1d(i)%a(j) = (VAR1 + VAR2) * i + j 
    PRINT *, "arr(",i,j,")= ", arrayOfp1d(i)%a(j) 
  END DO 
END DO 

Jim Dempsey

0 Kudos
Reply