Iso_c_binding: How to set double C pointer (an array) with a pointer to a Fortran array

I have pre-existing code that I am modifying to use the Fortran iso_c_binding standard. I’d like to know how many ways can you do it.

The below program compiles perfectly without any warnings using the iso_c_binding standard but doesn’t work.

Basically originally, on the Fortran side I had an integer defined. This integer got storage for an array via a C malloc. I have a similar variable (but a real) on the Fortran side which gets set to many values. The real is used to set values in what used to be an integer using C. How do I modify the below program to do this with iso_c_binding? Does the Fortran and/or C code have to change?

File main.f90:

       program main
       use the_interface
       use iso_c_binding, only : c_int,c_ptr,c_loc

       implicit none
       type(myint), pointer :: intvar
   
       type(c_ptr), pointer :: value_ptr

       type(real), allocatable, target :: value(:)
       type(c_ptr) :: myint_ptr 

       integer ans,i

       allocate(value(10),value_ptr)

       value_ptr = c_loc(value)

       do i = 1,10
         value(i) = real((i-1) * 3 + (i-1),4)
       end do

       ans = get_storage(myint_ptr)
       ans = set_source(myint_ptr,value_ptr)

       ans = print_source(myint_ptr)
       end program main

File test.c:

#include <stdio.h>
#include <stdlib.h>

typedef int int_type;

struct myint {
   int value;
};

#define NUM 10

int get_storage_ (int_type **dest)  
{
  *dest = (int_type *)malloc(NUM * sizeof(int_type));

  if (!*dest) {
      return -1;
  }

  return 0;
}

int set_source_ (struct myint **dest, struct myint *source)
{
  int i;
  int counter = 0;

  for (i = 0;i < NUM;i++)
  {
    /* (*dest + i)->value = i * 3 + i; */
     (*dest + i)->value = (source + i)->value;

    if (!(++counter % 5))
    {
      printf ("\n");
      counter = 0;
    }
  }

  return 0;
}

int print_source_ (struct myint **dest)
{
  int i;
  int counter = 0;
  int errors = 0;

  for (i = 0;i < NUM;i++)
  {
    if (((*dest + i)->value) != (i * 3 + i))
    {
      printf ("Error at %d %d != %d ",i, ((*dest + i)->value),(i * 3 + i));
      errors ++;
    }
    else
    {
      printf ("Value at %d = %d ",i,(*dest + i)->value);
    }

    counter++;

    if (!(counter % 5))
    {
      printf ("\n");
      counter = 0;
    }
  }

  printf ("\n%d values weren't set correctly!\n",errors);
  printf ("%d values were set correctly!\n\n",NUM - errors);

  return 0;
}

File the_interface.f90:

       module the_interface
       use iso_c_binding, only: c_int, c_ptr,c_float

       type, bind(c) :: myint
         integer(c_int) :: int_tmp
       end type

       type, bind(c) :: myfloat
         real(c_float) :: float_tmp
       end type

         function get_storage(address)bind(c,name="get_storage_")
             import :: c_int, c_ptr, myint
             implicit none
             type(c_ptr), intent(inout) :: address
             integer(c_int) :: get_storage
         end function

         function set_source(address, value)bind(c,name="set_source_")
             import :: c_int, c_ptr, myint
             implicit none
             type(c_ptr), intent(inout) :: address
             type(c_ptr), intent(inout) :: value
             integer(c_int) :: set_source
         end function

         function print_source(address)bind(c,name="print_source_")
             import :: c_int, c_ptr, myint
             implicit none
             type(c_ptr), intent(inout) :: address
             integer(c_int) :: print_source
         end function
       end interface

       end module the_interface

Compilation and Run:

  gcc      -g -c test.c     -o test.o 
  gfortran -g -c the_interface.f90 -o the_interface.o
  gfortran -g -c main.f90     -o main.o
  gfortran -g main.o test.o -o main.exe -lgfortran
  ./main.exe

Output:

Error at 0 38195424 != 0 Error at 1 38195424 != 4 Error at 2 38195424 != 8 Error at 3 38195424 != 12 Error at 4 38195424 != 16 Error at 5 38195424 != 20 Error at 6 38195424 != 24 Error at 7 38195424 != 28 Error at 8 38195424 != 32 Error at 9 38195424 != 36

You’re missing an interface statement in the_interface.f90, which is minor. The real issue is that your argument for set_source_ is not correct. Modify that function as:

int set_source_ (struct myint **dest, float **source)
{
  int i;
  int counter = 0;

  for (i = 0;i < NUM;i++)
  {
    /* (*dest + i)->value = i * 3 + i; */
     (*dest + i)->value = (*source)[i];

    if (!(++counter % 5))
    {
      printf ("\n");
      counter = 0;
    }
  }

  return 0;
}

and I get

$ gcc      -g -c test.c     -o test.o 
  gfortran -g -c the_interface.f90 -o the_interface.o
  gfortran -g -c main.f90     -o main.o
  gfortran -g main.o test.o -o main.exe -lgfortran
  ./main.exe


Value at 0 = 0 Value at 1 = 4 Value at 2 = 8 Value at 3 = 12 Value at 4 = 16 
Value at 5 = 20 Value at 6 = 24 Value at 7 = 28 Value at 8 = 32 Value at 9 = 36 

0 values weren't set correctly!
10 values were set correctly!

1 Like

Not as written. You might try exploring bind(c) derived types as that allows derived types in Fortran to mirror and be interoperable with C structs.

OP has applied the BIND(C) clause to the derived type declaration for the C struct’s. Use of C_F_POINTER intrinsic module procedure will thus help such as

..
call c_f_pointer( cptr=myint_ptr, for=intvar )
..

Then work with intvar as with any other object instance of a derived type in Fortran.