I am trying to pass a structure "stuff" from C to Fortran. "stuff" has a member "gef" which contains two variables, "name" and "extra". Before the call to the Fortran routine test2, both stuff.gef and stuff_gef are good, But when I enter the Fortran, the "name" and "extra" variables are bad.
(Ideally, I'd like to populate stuff.gef directly without having to go through an intermediary stuff_gef)
C code:
#include <iostream>
#include <cstddef>
#include <vector>
using namespace std;
extern "C" {
struct t_stuff_gef {
char name[256];
double extra;
};
struct t_stuff {
t_stuff_gef *gef;
};
void test2(t_stuff *stuff);
}
int main()
{
t_stuff stuff;
t_stuff_gef stuff_gef;
strcpy_s(stuff_gef.name, sizeof(stuff_gef.name), "Teststuff");
stuff_gef.extra = 100.0;
stuff.gef = &stuff_gef;
test2(&stuff);
}
Fortran code:
module ftncode_mod
use, intrinsic :: iso_c_binding
implicit none
type, public, bind(C) :: t_stuff_gef_ext
character(1) :: name(256)
real(8) :: extra
end type t_stuff_gef_ext
type, public, bind(C) :: t_stuff_ext
type(t_stuff_gef_ext) :: gef
end type t_stuff_ext
contains
subroutine test2(stuff_ext) bind(C)
!DEC$ATTRIBUTES DLLEXPORT :: test2
use, intrinsic :: iso_c_binding
type(t_stuff_ext), target , intent(in) :: stuff_ext
continue
return
end
end module
CodePudding user response:
The following code does what you want. The important point is to bind the Fortran types to C, and to declare a c_ptr
when a pointer is declared on the C side.
#include <iostream>
#include <cstddef>
#include <vector>
#include <string.h>
using namespace std;
extern "C" {
struct t_stuff_gef {
char name[256];
double extra;
};
struct t_stuff {
t_stuff_gef *gef;
};
void test2(t_stuff *stuff);
}
int main()
{
t_stuff stuff;
t_stuff_gef stuff_gef;
strncpy(stuff_gef.name,"Teststuff",256);
stuff_gef.extra = 100.0;
stuff.gef = &stuff_gef;
printf("%s\n%f\n\n",stuff_gef.name,stuff_gef.extra);
test2(&stuff);
}
module ftncode_mod
use, intrinsic :: iso_c_binding
implicit none
type, public, bind(C) :: t_stuff_gef_ext
character(256) :: name
real(c_double) :: extra
end type t_stuff_gef_ext
type, public, bind(C) :: t_stuff_ext
type(c_ptr) :: gef_cptr ! for consistency with the C struct
end type t_stuff_ext
contains
subroutine test2(stuff_ext) bind(C)
type(t_stuff_ext) , intent(in) :: stuff_ext
type(t_stuff_gef_ext), pointer :: gef
call c_f_pointer(stuff_ext%gef_cptr, gef) ! convert the C pointer to a Fortran pointer
write(*,*) gef%name
write(*,*) gef%extra
end
end module
Compilation:
%gfortran -c cfinteropf.f90 ; g cfinterop.cpp cfinteropf.o -lgfortran && a.out
Teststuff
100.000000
Teststuff
100.00000000000000
CodePudding user response:
My solution.
C Code:
#include <iostream>
#include <cstddef>
#include <vector>
using namespace std;
extern "C" {
struct t_stuff_gef {
char name[256];
double extra;
};
struct t_stuff {
t_stuff_gef gef;
};
void test2(t_stuff *stuff);
}
int main()
{
t_stuff stuff;
strcpy_s(stuff.gef.name, sizeof(stuff.gef.name), "Teststuff");
stuff.gef.extra = 100.0;
test2(&stuff);
}
Fortran Code:
module ftncode_mod
use, intrinsic :: iso_c_binding
implicit none
type, public :: t_stuff_gef_ext
character(1) :: name(256)
real(8) :: extra
end type t_stuff_gef_ext
type, public :: t_stuff_ext
type(t_stuff_gef_ext) :: gef
end type t_stuff_ext
contains
subroutine test2(stuff_ext) bind(C)
!DEC$ATTRIBUTES DLLEXPORT :: test2
type(t_stuff_ext), target , intent(in) :: stuff_ext
continue
return
end
end module