Back again after being away for quite a while...
After much searching, I found what I believe to be the closest answer to my problem is on stackoverflow (SO) at https://stackoverflow.com/questions/3852790/fortran-interface-to-call-a-c-function-that-return-a-poi..., (posted nearly 10 years ago!)
I quote this because using that example keeps the code simple and still illustrates my problem.
I want to return an array that has been created/memory allocated in C++ and be able to analyse the answer in Fortran, because that is where the bulk of the code for this application lies. My application goes off into C++ to produce the integer array answer and returns it to the Fortran program via the C interface. The original SO example used a single double precision variable as the return. I’ve changed it to integer because that is what I will be dealing with in my application. The example code (as changed) works.
I have highlighted with comments the changes that I have tried to make to return an array pointer, but I’ve run out of ideas. (I could say, “Oh for the bad old days when I could just equivalence an integer to an iarray(1) and go beyond the size of the array”, but I won’t. It’s good to have coding protections, but sometimes it gets frustrating.)
I am using Visual Studio 2017 and Intel Fortran parallel_studio_xe_2019_update5_composer.
My modified example of the original SO code:
[code here
! ps_test_pointers.f90
program foo
use, intrinsic :: iso_c_binding, only : c_ptr, &
c_f_pointer, &
c_int
implicit none
type(c_ptr) :: c_p!(:) ! <-------
integer(c_int), pointer :: f_p!(:) ! <-------
interface
function foofunc() bind(c)
import :: c_ptr
implicit none
type(c_ptr) :: foofunc!(:) ! <-------
end function foofunc
end interface
c_p = foofunc()
call c_f_pointer(c_p, f_p)
print *, f_p
end program foo
##########################################################
// ps_test_pointersC.cpp : 'Subroutine' only.
extern "C" {
int bar[3] = { 2, 3, 4 };
int *foofunc() {
return bar;
}
}
]
As I said above, the code works, in the sense that it prints out the first element of the array (‘2’).
If I add the ‘(:)’ to the definition of f_p, the code compiles without error, but when I run it, the program fails with the run-time error: “forrtl: severe (408): fort: (7): Attempt to use pointer F_P when it is not associated with a target” at the line “call c_f_pointer(c_p, f_p)”.
I have tried declaring c_p as an array (“c_p(:)”), but I get the same error in the same place.
I have also tried calling c_p as an argument to a subroutine – still only using integers
[code here
! ps_test_pointers.f90
program foo
use, intrinsic :: iso_c_binding, only : c_ptr, &
c_f_pointer, &
c_int
implicit none
type(c_ptr) :: c_p!(:) ! <-------
integer(c_int), pointer :: f_p!(:) ! <-------
interface
subroutine foofunc(c_p) bind(c)
import :: c_ptr
implicit none
type(c_ptr) :: c_p!(:) ! <-------
end subroutine foofunc
end interface
call foofunc(c_p)
call c_f_pointer(c_p, f_p)
print *, f_p
end program foo
##########################################################
// ps_test_pointersC.cpp : 'Subroutine' only.
extern "C" {
int bar[3] = { 2, 3, 4 };
void foofunc(int *rtn) {
rtn = bar;
}
}
]
but the created pointer in the C function never gets assigned to c_p on return (hence f_p is never defined).
Reading around the problem, I hope I’m not at the bleeding edge of compiler implementation and have exposed a problem between restrictions tightening but not coping with all the use cases!
Is there a solution to this? Any?
Contributions will be gratefully received!
Link Copied
I think you are working a trifle too hard here:
type(c_ptr) :: c_p(:) would be an array of C pointers, not a C pointer to a C array.
type(cptr) :: c_p is the one you need, because C returns an array and whatever is behind that address is of no interest to the declaration on the Fortran side, because C does not pass on this information.
To turn c_p into an array on the Fortran side use:
call c_f_pointer( c_p, f_p, [3] ) ! [3] is the shape of the resulting Fortran array - fill in whatever is needed
I may be overlooking something - I have not tried it, but this is the principle :).
I think you are working a trifle too hard here:
type(c_ptr) :: c_p(:) would be an array of C pointers, not a C pointer to a C array.
type(cptr) :: c_p is the one you need, because C returns an array and whatever is behind that address is of no interest to the declaration on the Fortran side, because C does not pass on this information.
To turn c_p into an array on the Fortran side use:
call c_f_pointer( c_p, f_p, [3] ) ! [3] is the shape of the resulting Fortran array - fill in whatever is needed
I may be overlooking something - I have not tried it, but this is the principle :).
Thank you Arjen_Markus. That's part of the solution. I hinted that this example is a very simplified form of my problem that illustrates the issue. That said, I do have the size of the array in the returned pointer array and I can extract that and return it in the foofunc (or more accurately for a Fortran user, foosub).
I still have some more work to do to get all the info back into the Fortran program, but you have broken my deadlock. Thanks.
Yep. All working now.
For more complete information about compiler optimizations, see our Optimization Notice.