3

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?

0

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.