Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.
29285 Discussions

Can dynamically allocated array be passed back to C++?

Darrell
Beginner
1,451 Views
Is it possible (with or without Fortran 2003 ISO_C_BINDING) to dynamically allocate an output array in a fortran subroutine and pass it back to a C++ program as shown below? Using Intel Fortran v11.059 for Mac OS X 10.5.7, I get a segmentation fault on the allocate statement using the compiler settings as shown below.

icpc -g -traceback -c driver.cpp
ifort -check -g -traceback -nofor-main -lstdc++ test.f driver.o

c Return a dynamically allocated array from Fortran back to C++
subroutine foo(nmax, in, out)
integer nmax
real in(nmax)
real, dimension(:), pointer :: out
integer ierr

allocate(out(nmax), stat=ierr)
print *,'allocate returned: ',ierr

do i=1,nmax
out(i) = in(i) * i
print *,i,in(i),out(i)
end do

return
end subroutine foo

#include

extern "C" void foo_(int* nmax, float* in, float* out);

// Test dynamically allocated fortran output array from C++
int main()
{
int nmax = 10;
float* in = new float[nmax];
float* out = NULL;

for (int i = 0; i < nmax; i++)
in = (i + 1) * 10.0;

foo_(&nmax, in, out);
std::cout << "Done!" << std::endl;

delete[] in;
delete[] out;

return 0;
} // end main


0 Kudos
8 Replies
Steven_L_Intel1
Employee
1,451 Views

A C pointer is not the same as a Fortran assumed-shape array, and if you allocate the data in Fortran it must be deallocated in Fortran.

The Fortran code could look something like this:

[plain]subroutine foo(nmax, in, outp)
use, intrinsic :: iso_c_binding
integer nmax
real in(nmax)
type(C_PTR) :: outp
real, dimension(:), pointer :: out
integer ierr

allocate(out(nmax), stat=ierr)
print *,'allocate returned: ',ierr

do i=1,nmax
out(i) = in(i) * i
print *,i,in(i),out(i)
end do

outp = C_LOC(out)

return
end subroutine foo[/plain]

On the C++ side, you need to make sure that you pass the address of a pointer to the Fortran code, so that Fortran can store the allocation address into the pointer. Note that one should use POINTER and not ALLOCATABLE here - the POINTER won't get automatically deallocated on exit.
0 Kudos
Hirchert__Kurt_W
New Contributor II
1,451 Views
On the C++ side, you need to make sure that you pass the address of a pointer to the Fortran code, so that Fortran can store the allocation address into the pointer.

Just so there is no confusion, let me say this another way. What you pass from the C++ side needs to be float**, not float* (e.g., &out rather than just out). Alternatively, you could make foo a function that returns a pointer and assign it to out on the C++ side.

Two other minor cautions:

1. You are assuming that a Fortran procedure named FOO will appear to have the name foo_ to C (and thus appear to be a C external named foo_ to C++). This is the most common implementation decision, but it is not universally true, and indeed it isn't even always true for ifort. You might want to use a BIND(C,NAME='...') clause to force the Fortran procedure to have a particular C name.

2. Theoretically, storage allocated to a Fortran POINTER could be discontiguous. (Fortran POINTERs are capable of pointing to discontiguous arrays, and there is no requirement in the Fortran standard that storage allocated to a POINTER be contiguous.) If any Fortran implementation actually did that, this whole scheme would fall apart. I know of no existing compiler that does this, nor do I know of any possible reason why a compiler might find it desirable to do so, but that doesn't mean there will never be such a reason. Its all a question of how paranoid you want to be about future changes in the computing environment. One possible alternative would be to replace the ALLOCATE statement with a reference to malloc (using C interoperability), thus concpetually allocating the storage in C and making it contiguous. As an added benefit, storage allocated this way could be deallocated using free, either from Fortran or from the C++ side.

-Kurt
0 Kudos
Steven_L_Intel1
Employee
1,451 Views
If you just ALLOCATE a pointer array, it will be contiguous. You're right that a POINTER can get assigned a non-contiguous array slice, but you have to go out of your way to do that. The Fortran 2003 C interoperability features provide all you need to do this right.
0 Kudos
Darrell
Beginner
1,451 Views
I'm loving Fortran 2003's ISO_C_BINDING more & more. However, the customer currently insists on using ifort v9. [My previous post you mentioned this new feature requires ifort v10.1 or greater.] We're happy with Intel Fortran 11 & think everyone should upgrade! :-)

I was able to get my little example working using non standard Cray Pointers (circa Fortran 1978?). Using float** for the C++ as discussed in this post. I thought I was going to be headed for a long three day weekend when the compiler said it didn't like my converting F90 pointers to cray pointers inside the module level interface block?!?

Back to google looking for related posts, I see a previous discussion here at Intel regarding CVF_1D_DESCRIPTOR so that C can use the F90 pointers but can't find the definition of the data type struct for it and CVF_2D_DESCRIPTOR?

It looks like I could leave the F90 program as-is & teach the C++ driver how to use the pointers vs convert the Fortran and it's interface block from F90 pointers to Cray pointers while waiting for everyone to get on the ISO_C_BINDING band wagon so I can conver the whole thing once and for all? [Plan B is to update ftoc for F90 to C++.] Thanks!

0 Kudos
Steven_L_Intel1
Employee
1,451 Views
Forget playing with descriptors. It can be done but it's not worth the hassle. Also, CVF used a different layout than Intel Fortran does. (It is documented in the "Building Applications" section of the manuals.)

You can do it with Cray pointers. Something like this:

[plain]subroutine foo(nmax, in, outp)
integer nmax
real in(nmax)
real, dimension(*) :: out
pointer (outp, out)
integer ierr

outp = malloc(nmax*4)

do i=1,nmax
out(i) = in(i) * i
print *,i,in(i),out(i)
end do


return
end subroutine foo[/plain]

0 Kudos
Darrell
Beginner
1,451 Views
Thanks for the warning about trying to teach C++ F90 pointers. I found the page in the documentation under Mixed Language Support, Array Descriptors. That would have been a huge frustrating waste of time especially since as you note Compaq & Intel have different internals for the pointers!

Thanks for the tip on the interface block with Cray pointers. I found the page in the documentation right after the above page under Integer Pointers. The example includes the interface block. Basically, the interface block calls the pointer an integer then the pointer and pointee are declared in the code. Thanks!

Note in the above example in the documentation, the C code does not pass the address of the pointer as we'd discussed above just int* p not &p; the example works so I'll press on converting the program. I'll put in a requirement to get everyone upgraded to the latest Intel Fortran that supports the Fortran 2003 ISO_C_BINDING way of doing things. Thanks!
0 Kudos
Charliey
Beginner
1,451 Views
As mentioned in Fortran books and also in this thread,
Fortran pointers and C pointers aren't compatible per
se. For instance, Fortran pointers can point to
non-contiguous memory when they are used to slice a
multi-dimensional buffer. The question I have is as
follows: Assuming that the Fortran pointer is only
used to dynamically allocate an array of integer, real,
or double precision type (i.e. the pointer always
points to contiguous memory where simple intrinsic
types are stored), will the following code work:

main.f90:
program pointertest
integer*8 :: iattr
real, dimension(:), pointer :: data
integer :: n
n = 100
allocate(data(n))
! fill buffer etc
iattr = loc(data(1))
print *,'F: ',iattr
call my_c_function(data,n)
deallocate(data)
end program

my_c_function.c:
#include
void my_c_function_(float *data, int *n) {
printf("C: %lldn",(long long)data);
/* do something with data buffer */
return;
}

This example seems to work fine (i.e. data can be properly
accessed without problems in the C world) and provides a
consistent output. Is it just luck or will it always be the
case as long as the assumptions (see above) are met? In
other words, will this code always pass the correct address
(where the data starts) to C?

The new ISO_C_BINDING would certainly allow to handle this
situation in a somewhat cleaner way, I know.

Where is the additional information that belongs to a
Fortran pointer (stride information, length information, etc)
actually stored? A Fortran pointer doesn't seem to behave
like a structure/class...

0 Kudos
Steven_L_Intel1
Employee
1,451 Views
A Fortran pointer to an array is, internally, the address of a data structure whose layout is described in the mixed-language programming section of the documentation. There is no straightforward way of seeing this from Fortran without "cheating".
0 Kudos
Reply