interoperate with derived type

I have three structs in C

typedef struct TYPE1_T {
    type2_t params;
    type3_t *status; 
} type1_t;
typedef struct TYPE2_T {
    int idx;	
    int size;
} type2_t;

typedef struct TYPE3_T {
    int idx;	
    int size;
} type3_t;

Is it possible to create an interoperable types in Fortran 2003? I’m not sure as there is one pointer in the second component of type1_t.

Tuan

Hi Tuan

Is it possible to create an interoperable types in Fortran 2003?

I fairly sure that pointers and allocatable are not allowed in bind(c) derived types. I also don’t think you can have other derived types either since each component of a derived type must ne an ISO_C_BINDING interoperable type. Though, I’ll ask when I’m back in the office on Monday since I’m not positive.

  • Mat

Thanks, Mat. This is important as it is quite common in C code that I have.

Waiting for your confirmation.
Tuan

Hi Tuan,

This was a good exercise for me since I haven’t had the opportunity to work with ISO_C_BINDING. I was correct in that you can’t have derived type pointers but you can use regular C_PTR types and then use the function “c_f_pointer” to associate the C pointer with a local pointer to the derived type.

For example, main.c

#include <stdio.h>
#include <malloc.h>

#define N 10

typedef struct TYPE3_T {
    int idx;
    int size;
} type3_t;

typedef struct TYPE2_T {
    int idx;
    int size;
} type2_t;

typedef struct TYPE1_T {
  type2_t params;
  type3_t *status;
  int idx;
} type1_t;


void fortran_func_(type1_t *, int);

int main() {

  type1_t A;

  A.idx = 0;
  A.params.idx = 0;
  A.params.size = 0;
  A.status = (type3_t*) malloc(sizeof(type3_t) * N);
  for (int i=0; i<N; ++i) {
    A.status[i].idx = 0;
    A.status[i].size = 0;
  }

  fortran_func_(&A, N);

  printf("%d %d %d\n", A.idx, A.params.idx, A.params.size);
  for (int i=0; i<N; ++i) {
   printf("%d %d\n", A.status[i].idx, A.status[i].size);
  }

  free(A.status);

}

fexam.f90

subroutine fortran_func (Aptr, N)

  use iso_c_binding

  type, bind(c) :: type2_t
     integer(c_int) :: idx, size
  end type type2_t

  type, bind(c) :: type3_t
     integer(c_int) :: idx, size
  end type type3_t

  type, bind(c) :: type1_t
     type(type2_t) :: params
     type(c_ptr) :: status
     integer(c_int) :: idx
  end type type1_t

  type(c_ptr), value :: Aptr
  type(type1_t), pointer :: A
  type(type3_t), dimension(:), pointer :: status
  integer, value :: N
  integer :: i
  
  ! Assocatate the C "Aptr" pointer to a local fortran
  ! pointer to type1_t
  call c_f_pointer(Aptr,A)

  A%idx = 1
  A%params%idx = 100
  A%params%size = 1000
  
  ! Associate the 'status' type1_t pointer to A%status
  ! You must add a shape arguement (N) since its an array
  call c_f_pointer(A%status,status,N)

  do i=1,N
     status(i)%idx = i
     status(i)%size = N-i
  enddo
  
end subroutine fortran_func

Running the code

% pgf90 -c fexam.f90
% pgcc -pgf90libs main.c fexam.o
main.c:
% a.out
1 100 1000
1 9
2 8
3 7
4 6
5 5
6 4
7 3
8 2
9 1
10 0

I’ll work on the inverse (Fortran calling C) next.

  • Mat

Here’s the inverse, Fortran calling C

fmain.f90

module type_mod

  use iso_c_binding

  type, bind(c) :: type2_t
     integer(c_int) :: idx, size
  end type type2_t

  type, bind(c) :: type3_t
     integer(c_int) :: idx, size
  end type type3_t

  type, bind(c) :: type1_t
     type(type2_t) :: params
     type(c_ptr) :: status
     integer(c_int) :: idx
  end type type1_t

end module type_mod

program test_f_to_c

  use iso_c_binding
  use type_mod

  interface 
     subroutine init_data(A,N) bind(c)
       use iso_c_binding
       use type_mod
       type(type1_t) :: A
       integer(c_int), value :: N
     end subroutine init_data
  end interface
  
  integer, value :: N
  parameter(N=10)
  type(type1_t) :: A
  integer :: i
  type(type3_t), dimension(:), pointer :: status

  call init_data(A,N)

  print *, A%idx, A%params%idx, A%params%size
  call c_f_pointer(A%status,status,N)
  do i=1,N
     print *, i, status(i)%idx, status(i)%size
  enddo
     
end program test_f_to_c

init.c

#include <stdio.h>
#include <malloc.h>


typedef struct TYPE3_T {
    int idx;
    int size;
} type3_t;

typedef struct TYPE2_T {
    int idx;
    int size;
} type2_t;

typedef struct TYPE1_T {
  type2_t params;
  type3_t *status;
  int idx;
} type1_t;


void init_data(type1_t * A, int N) {

  A->idx = 1;
  A->params.idx = 10;
  A->params.size = 100;

  A->status = (type3_t*) malloc(sizeof(type3_t) * N);
  for (int i=0; i<N; ++i) {
    A->status[i].idx = i+1;
    A->status[i].size = N-i;
  }

}



% pgf90 fmain.f90 init.c ; a.out
fmain.f90:
init.c:
            1           10          100
            1            1           10
            2            2            9
            3            3            8
            4            4            7
            5            5            6
            6            6            5
            7            7            4
            8            8            3
            9            9            2
           10           10            1

Could you please check the init.c code, it’s written in Fortran, right? I guess you copied the wrong text.

Tuan

Sorry, cut and paste error. I went back an updated the code.

  • Mat

Hi Mat,
May I ask you the interoperating type in C of a Fortran device data

double precision, dimension(:), device:: data

Suppose that I have the function (in C) that return data on device memory

void foofunc(uint64_t* d_data, n) {
    cudaMalloc(d_data, sizeof(uint64_t) * n)
    ...
}

To interoperate with this function in Fortran. I tried this yet it doesn’t work?

subroutine fortranfunc()
double precision, dimension(:), allocatable, device :: d_data
interface 
   subroutine foofunc(d_data, n) bind(c) 
       use iso_c_binding
       integer(c_int64_t) :: d_data
       integer(c_int) :: n
   end subroutine
end interface

n = 10
call foofunc(d_data, n)

end subroutine

Thanks,
Tuan

Hi Tuan,

I think all you need to do is to change

   subroutine foofunc(d_data, n) bind(c)
       use iso_c_binding
       integer(c_int64_t) :: d_data
       integer(c_int) :: n
   end subroutine

to

   subroutine foofunc(d_data, n) bind(c)
       use iso_c_binding
       double precision, dimension(:), device :: d_data 
       integer(c_int) :: n
   end subroutine

A “device” array is really just a CUDA C device pointer so don’t need the ISO_C_BINDING here.

  • Mat
    [/code]

Thanks, Mat.

Could you please help me to make clear what’s the difference between using

double precision, dimension(:), device :: d_data

with the new type type(cudaArrayPtr)

type(cudaArrayPtr) :: d_data

that I read in the manual. Unfortunately, there is no documentation on this datatype. So I’m not sure on using it.

Thanks a lot,
Tuan