Reputation: 458
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?
Upvotes: 1
Views: 417
Reputation: 299
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.
Upvotes: 2