Reputation: 127
I have a Fortran derived type with a matching C struct. I am creating an array of the derived type, and I want to pass this array to a C function. Here is my Fortran code
module tmod
use iso_c_binding
integer, parameter :: nstring = 22
integer, parameter :: ntype = 3
!! Type with a bind(c)
type, bind(c) :: mystruct
character (c_char) :: hello (nstring)
character (c_char) :: world (nstring)
integer (c_int) :: ii
end type mystruct
interface !! Function on the C side
integer (c_int) function testfunc (t, asize) bind(C, name="testfunc")
use iso_c_binding
import mystruct
type (mystruct) :: t (:) !< array of struct/derived type
integer (c_int), value :: asize !< Size of the array
end function testfunc
end interface
end module tmod
program testprog
use tmod
use iso_c_binding
type(mystruct), target :: t (0:ntype-1) !< Array of derived type set up with C-like indicies
integer (c_int) :: i
!! Initialize the struct array
do j = 0,ntype-1
t(j)%hello = "HelLo"//c_null_char
t(j)%world = "World"//c_null_char
t(j)%ii = j-3
enddo
!! Call C function
i = testfunc(t, ntype)
end program testprog
My C code loops through the struct and prints the integer
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int const nstring = 22; /* matches string size from fortran */
struct mystruct{
char hello[22];
char world[22];
int ii ;
}mystruct;
int testfunc (struct mystruct * t[], int asize ) {
/* Loop through array and print index then ii value */
for (int j = 0; j < asize ; j++) {
printf ("%d \n", j);
printf ("%d \n", t[j]->ii);
}
return 0;
}
These compile with icc version 2021.5.0 and ifort version 2021.5.0 or gcc version 11.2.0 (this drops a note about declared here for mystruct t[]). It prints out the result of the first loop. It gives me a segmentation fault on the second pass through the loop. I can't pass the array by value because it is an array and that's apparently not allowed.
If I define my C function without the pointer
int testfunc (struct mystruct t[], int asize )
the program runs to the end, but does not print the correct information. How can I pass this Fortran array to the C function?
Upvotes: 3
Views: 534
Reputation: 32451
Your Fortran interface has an assumed-shape dummy argument t
. The interface for testfunc
with this assumed-shape array is not interoperable with the formal parameter struct mystruct * t[]
of the C function. An interoperable C function must have the argument associated with a pointer to CFI_cdesc_t
object.
That said, there's little reason in the code you show to want to use an assumed-shape dummy argument, seeing as you are also passing the size of the array. Instead use an explicit-shape argument:
interface !! Function on the C side
integer (c_int) function testfunc (t, asize) bind(C, name="testfunc")
use iso_c_binding
import mystruct
integer (c_int), value :: asize !< Size of the array
type (mystruct) :: t (asize) !< array of struct/derived type
end function testfunc
end interface
Which is then going to lead to the next problem: you don't want struct mystruct * t[]
but struct mystruct *
:
int testfunc (struct mystruct *t, int asize ) {
/* Loop through array and print index then ii value */
for (int j = 0; j < asize ; j++) {
printf ("%d \n", j);
printf ("%d \n", t[j].ii);
}
return 0;
}
Alternatively, if for some reason you do need the assumed-shape argument:
interface !! Function on the C side
integer (c_int) function testfunc (t) bind(C, name="testfunc")
use iso_c_binding
import mystruct
type (mystruct) :: t (:) !< array of struct/derived type
end function testfunc
end interface
you can make that interoperable with
int testfunc (CFI_cdesc_t* t) {
/* Loop through array and print index then ii value */
struct mystruct *address;
CFI_index_t subscript[1];
for (int j = 0; j < t->dim[0].extent ; j++) {
subscript[0] = j;
printf ("%d \n", j);
address = (struct mystruct *) CFI_address(t, subscript);
printf ("%d \n", address->ii);
}
return 0;
}
Either way, you'll probably want to address the assignments like
t(j)%hello = "HelLo"//c_null_char
which sets the array t(j)%hello
to 22 elements of "H"
. You'll want the right-hand side to itself be an array of 22 elements, say:
t(j)%hello = [character(c_char) :: "H", "e", "l", "L", "o", c_null_char, [(" ", i=1,16)]]
And don't forget to use implicit none
liberally to reduce the surprise potential.
Upvotes: 2