- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Tags:
- 2D array
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
// 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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page