简体   繁体   中英

C to Fotran: Interoperability of a C pointer to a char array, declared in a struct

I am creating Fortran bindings to a C library. The C header defines a struct as follows:

struct frame {
  char *foo[4];
  int  bar;
};

(in reality, the C struct is much more complicated, I just stripped it down to the essential part). Now, the C library also provides a function that gives values to the struct. For the purposes of this question, I wrote a simplified replacement of such a function:

void setFrame(struct frame *fr) {
  fr->bar=12;
  printf("bar in C: %d\n", fr->bar);
}

(I don't really care about the value of foo , but I do care about the value of bar , and I won't get it correctly on the Fortran side if foo isn't declared properly). Now, this is my Fortran implementation of the above:

module binding
use iso_c_binding
implicit none
private

type, bind(c), public :: frame
  integer(kind=c_int), dimension(4) :: foo
  integer(kind=c_int) :: bar
end type Frame

public :: setFrame

interface
  subroutine setFrame_C(fr) bind(c, name="setFrame")
  import :: c_ptr
  type(c_ptr), value :: fr
  end subroutine setFrame_C
end interface

contains

subroutine setFrame(fr)
type(frame), intent(inout), target :: fr
call setFrame_C(c_loc(fr))
end subroutine setFrame

end module binding

and this is a test Fortran program:

program test
use binding
implicit none
type(frame) :: fr
print "(a,i0)","bar in Fortran (before calling setFrame): ",fr%bar
call setFrame(fr)
print "(a,i0)","bar in Fortran (after calling setFrame): ",fr%bar
end program test

Running the above program gives:

bar in Fortran (before calling setFrame): -1076110840
bar in C: 12
bar in Fortran (after calling setFrame): 12

meaning the Fortran binding works. Note that the equivalent of char *foo[4] is an integer array of c_int . This is a bit surprising because, although there is not much of a difference between a C integer and a C char, I would expect C char to be a one-byte integer. Now, if I declare char *foo[4] in a more "natural" way:

character(kind=c_char), dimension(4) :: foo

it doesn't work (I don't get the correct value for fr.bar ). The reason is that, at least in gfortran 11.2.0, c_int is actually equal to 4 while c_char is equal to 1. Even character(kind=c_int) works. I fact, character(kind=4) also works, although the compiler (correctly) issues a warning in this case.

So my question is... what is actually the correct, "formal" way to treat char *foo[4] on the Fortran side, portable to another compiler that probably treats C char in another way?

The problem is that char *foo[4]; is an array of four pointers to char, so your interoperable declaration in Fortran has to be

type, bind(c), public :: frame
  type(c_ptr), dimension(4) :: foo
  integer(kind=c_int) :: bar
end type Frame

Your way only happens to work because you are probably testing on a 32bit platform where a pointer has the same width as an int.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM