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

Fortran-C++ interoperation of 2D array

AONym
New Contributor II
1,410 Views

I have a C++ pgm that uses Fortran subroutines and functions, but I am having trouble converting one function call to multi-threaded, due to confusion as to how to pass an array reference.

This is the original code, which works.

! Fortran function, called from C++
FUNCTION Compute(nPoints)
INTEGER, INTENT(OUT), DIMENSION(n, m) :: nPoints
-------------------------------------------------------
// C++ prototype
extern "C" int __cdecl COMPUTE(int nP[m][n]);

// Declaration of C++ array
int nPoints[m][n];

// Call of Fortran function
int error = COMPUTE(nPoints);

The above compiles, and works as I expect.
The C++ array nPoints receives the output from Fortran function Compute.


====================== End of original code =================================


I'm trying to modify the above so Compute runs in a separate thread. So I need to pass the array from the C++ function that declares the nPoints array to another thread.

I create a new structure to hold something that describes the C++ array.

struct ParamsForThread
{
int *nPoints;
};
ParamsForThread p;
p.nPoints = &nPoints[]0[0];

// Call of Fortran function from thread; p is the ParamsForThread structure.
int error = COMPUTE(p->nPoints);

The C++ compiler gives an error:
error C2664: 'int COMPUTE(int [][n])': cannot convert argument 1 from 'int *' to 'int [][n]'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast

How should I define the fields in the ParamsForThread structure so I can use it in the Fortran function call?

 

Labels (1)
0 Kudos
6 Replies
jimdempseyatthecove
Honored Contributor III
1,383 Views

Try

// C++ prototype
extern "C" int __cdecl COMPUTE(void*);

int ret = COMPUTE(&nPoints);
int ret = COMPUTE(&nPoints[0][0]);

Presumably m and n are fixed parameters and are in agreement between the C++ side and Fortran side.

 

Most 2D arrays on the C++ side use type**, and the data may be discongiguous. You can have a contiguous block of data with literal dimensions and a contiguous block of data (row major for Fortran other way for C/C++).

 

Jim Dempsey

0 Kudos
AONym
New Contributor II
1,360 Views

Fortran needs to know the array extents. That's why the Fortran function contains the (fixed) values for m and n.

In the original code, I just prototyped the Fortran function as int nPoints[m][n], and, as I said, this works fine.

The problem is, how to transfer the extents to the structure that transmits info from my C++ pgm to the thread (ParamsForThread in my example). In other words, I want to match the prototype's arg int nPoints[m][n].

As far as using the type int **, how do I get from the C++ declaration int nPoints[m][n] to a variable of type int **? And, even if this could be done, the C++ code would be obscured, since the prototype of the Fortran function would now have to be COMPUTE(int **something).

Since the C++ array is declared with constant dimensions, it will be contiguous.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,382 Views

Use the debugger, place a signature value in the 1st element [0][0] (e.g. 123456789), and say -123456789 in [1][0] and step into the called function. See what you get for (1,1) and (1,2). You can experiment with small numbers for m and n as it will be easire to examing the entire blob being passed. When you get it right, up the values for m and n

 

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,317 Views

jimdempseyatthecove_0-1698765930562.png

// ConsoleApplication11.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>

extern "C" int COMPUTE(int* array, int m, int n);

int main()
{
    std::cout << "Hello World!\n";
    int m = 10;
    int n = 20;
    int* blob = (int*)malloc(m*n*sizeof(int));
    int ret = COMPUTE(blob, m, n);
}
function Compute(array, m, n) bind(C, name="COMPUTE") result(ret)
    implicit none
    integer, intent(inout) :: array(n,m)
    integer, value, intent(in) :: m,n
    integer :: ret
    
    integer :: i,j
    
    do i=1,m
        do j=1,n
            array(j,i) = j*i
        end do
    end do
    ret = sum(array)
end function Compute

I will let you work out the details of accessing the array on the C++ side (e.g. either passing the address of array[0][0] or the blob)

 

There is also a chapter on how to use Fortran descriptors in the user manual.

 

Jim Dempsey

0 Kudos
AONym
New Contributor II
1,309 Views

Jim -

Thanks for your detailed answer; I understand how this will work. I was hoping for something simpler (like my original code), not having to pass the extents explicitly.

I have realized that my difficulty is with the C++ side, namely, a way to store the extents and array address in a single entity, to match the original Fortran prototype.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,296 Views

If, on the C++ side, you create (or find) an array type/class, it will contain a pointer (or a member function to get a pointer) to the memory block and the index extents for each dimension).

You can then write a shell function that accepts this type, extracts the base, and index extents, then pass it on to the Fortran routine.

An alternative is to pass the address of the C++ type to the Fortran routine as an interoperable type (generally a Fortran type with BIND(C)) then use a call to C_TO_F_POINTER to build an array descriptor for use on the Fortran side.

 

Jim Dempsey

0 Kudos
Reply