I would like to write functions or subprograms that return a derived type containing arrays with a size that is unknown at compile time. The rank is known. I would like to call these from C. One method on the fortran side is perhaps to use a parameterised derived type. Can I call this from C? Is there another preferred method? Can I make this a function rather than a subroutine? My fortran code so far is given below:
module dt
use iso_c_binding
implicit none
type, bind(c) :: results(n)
integer(c_int), len :: n
real(c_double), dimension(n) :: myarray
end type results
contains
subroutine set_results(res, n)
use iso_c_binding
integer(c_int) :: n
integer(c_int) :: i
type(results(n)), intent(out) :: res
do i=1,n
res%myarray(i) = i
end do
end subroutine set_results
end module dt
program use_dt
use dt
implicit none
type(results(10)) :: myresults
call set_results(myresults, 10)
print *,myresults
end program use_dt
Let's assume for the time being that you can allocate storage of the correct size in the calling program (the C++ desktop application). In other words all dimensions are known before entering the Fortran subprogram.
For interoperable types, the easiest option is to use assumed-size arrays, meaning array dimensions are need to be passed explicitly (or are assumed to be known constants):
For a 2D array of size n × m, your procedure interface might look like this:
The integer
ldacan be used to account for any array padding. With 2D arrays you need to be careful with differences due to opposite storage order, i.e. Fortran (column-major) vs C/C++ (row-major). Arguably, it's easier to assume column-oriented storage in C and do multi-dimensional indexing manually if necessary.The newer, but potentially more powerful option is to the use the C descriptors introduced in Fortran 2018. Descriptors can be used to create Fortran objects in C/C++ which have no direct equivalent. Descriptors allow you to interoperate also with assumed-shape, allocatable, and pointer arrays.
Using an assumed-shape array, your routine is written as
Here's how the calling the Fortran procedure will look like in C++:
If you'd like to pass a 2-dimensional assumed-shape array you just need to adjust the rank-constant (replace 1 with 2), and adjust the extents, e.g.
It is also possible to pass array slices, provide custom array bounds, and more. To learn more about this approach I would recommend reading Metcalf, Reid, & Cohen (2018), Modern Fortran Explained, Oxford University Press, pgs. 397-421.
Upon reading the OP's comment, here is a take at a solution where the C side is responsible for allocating buffers encapsulated in a struct, and on the Fortran we create operate on a derived type which shadows the C struct:
(I haven't tested this, so apologies if any errors remain)
In your main C++ app, you would then call the procedure as follows:
You will probably want to use the RAII idiom, and wrap the C struct in a C++ class which will do the deallocation once the struct goes out of scope.