How do you write an Fortran interface for a C struct that contains other arrays of structs?
I wrote a small test to understand how to interface Fortran to a C struct that contains an array(s) of structs. This was to understand why a library I am wrapping using Fortran keeps seg faulting.
In this particular test, the derived type on the Fortran side does not print the same values as the C
side!
This is the C side:
aos.h
#include <stdlib.h>
#include <stdio.h>
struct Foo_ {
float pos[2];
float cutoff;
};
typedef struct Foo_ Foo;
struct Bar_ {
float pos[2];
float centre;
};
typedef struct Bar_ Bar;
struct FooBar_ {
int maxg;
int* gorder;
Foo f[2];
Bar b[2];
};
typedef struct FooBar_ FooBar;
void init_default_values(FooBar* fb);
aos.c
#include "aos.h"
void init_default_values(FooBar* fb) {
fb = malloc( sizeof(FooBar) );
fb->maxg = 1;
fb->gorder = malloc( sizeof(int) );
*(fb->gorder) = 2;
printf( "fb->maxg = %d\n", fb->maxg );
printf( "fb->gorder = %d\n", *(fb->gorder) );
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
fb->f[i].pos[j] = i+j*2;
printf( "fb->f[%d].pos[%d] = %f\n", i, j, fb->f[i].pos[j] );
}
}
}
Here is my Fortran attempt:
faos.f90
module mod_aos
use iso_c_binding
use iso_fortran_env, only: f32 => real32, f64 => real64
implicit none
type, bind(c) :: Foo
real(f32) :: pos(2)
real(f32) :: cutoff
end type Foo
type, bind(c) :: Bar
real(f32) :: pos(2)
real(f32) :: centre
end type Bar
type, bind(c) :: FooBar
integer(c_int) :: maxg
type(c_ptr) :: gorder
type(Foo) :: f(2)
type(Bar) :: b(2)
end type FooBar
interface
subroutine init_default_values( fb ) bind(c, name="init_default_values")
import:: FooBar
type(FooBar), intent(inout) :: fb
end subroutine init_default_values
end interface
contains
end module mod_aos
program main
use iso_c_binding
use mod_aos
implicit none
type(FooBar) :: fb
print *, "======"
print *, "C SIDE"
print *, "======"
call init_default_values( fb )
print *, ""
print *, "============"
print *, "FORTRAN SIDE"
print *, "============"
print *, "fb%maxgeom = ", fb%maxg ! Should be 1
print *, "fb%gorder = ", fb%gorder ! Should be 2
print *, "fb%f%pos = ", fb%f(1)%pos ! Should be [0.0, 2.0]
print *, "fb%f%pos = ", fb%f(2)%pos ! Should be [1.0, 3.0]
end program main
Here is the output. notice that the Fortran side prints the wrong values (random values):
======
C SIDEfb->maxg = 1
fb->gorder = 2
fb->f[0].pos[0] = 0.000000
fb->f[0].pos[1] = 2.000000
fb->f[1].pos[0] = 1.000000
fb->f[1].pos[1] = 3.000000============
FORTRAN SIDEfb%maxgeom = 0
fb%gorder = 0
fb%f%pos = 2.80259693E-45 0.00000000
fb%f%pos = -0.00000000 0.00000000