cfortranfortran-iso-c-binding

With iso_c_binding, sending c_ptr to C, malloc,, set value


Question: How do I get the 28 back to Fortran?

main.f90:

       program test

       use play_dice_game
       use iso_c_binding, only : c_ptr, c_f_pointer, c_null_ptr

       type(c_ptr) :: test_ptr
       integer, pointer :: ftest_ptr
       integer result

        test_ptr = c_null_ptr

        result = roll_dice(test_ptr)

        call c_f_pointer(test_ptr, ftest_ptr)

        write(*,*) 'C test pointer is ',test_ptr
        write(*,*) 'Fortran test pointer is ',ftest_ptr

       end program test

play_dice_game.f90:

       module play_dice_game

       use, intrinsic :: iso_c_binding, only: c_int, c_ptr

       interface
         function roll_dice(test_p) bind(c, name="roll_dice_")
             import :: c_int, c_ptr
             implicit none
             type(c_ptr), intent(out) :: test_p
             integer(c_int) :: roll_dice
         end function
       end interface

       end module play_dice_game

test.c:

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

int roll_dice_(int *test) {

    if (!*test)
    {
      printf ("Creating storage for test\n");
      test = malloc(sizeof(int));

      *test = 28;
      printf ("variable test is set to %d\n\n",*test);
    }
    else
    {
      printf ("test is not null\n");
    }

    return 0;
}

Compile and Run Program:

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

Output:

Creating storage for test variable test is set to 28

C test pointer is 0 Fortran test pointer is 0

I tried using c_f_pointer without success. But, if I define in the C file a struct with an int and reference it in the Fortran file I can get the "28" in Fortran. I'd like something simple.

In C:

struct myint {
   int int_tmp;
};

int roll_dice_(struct myint **test) 
...
    *test = malloc(sizeof(**test));
...
    (*test)->int_tmp = 28;

In Fortran:

       type(myint), pointer :: intvar
       call c_f_pointer(myint_ptr, intvar)

       write(*,*) 'my int = ', intvar%int_tmp

Solution

  • I have no idea how fortran works, but your C function has a problem. You allocate memory and then loose the pointer to that memory.

    I suggest you take an int ** in roll_dice_ instead to be able to return a pointer to the allocated memory. Example:

    #include <stdio.h>
    #include <stdlib.h>
    
    int roll_dice_(int **test) {
        if(!test) {
            puts("test is null");
            return 0;             // 0 for fail
        }
    
        if(!*test) {
            printf("Creating storage for test\n");
            *test = malloc(sizeof **test);
    
        } else {
            printf("*test is not null\n");
        }
    
        **test = 28;
        printf("variable test is set to %d\n\n", **test);
    
        return 1;                 // 1 for success
    }
    

    Possible output:

    Creating storage for test
    variable test is set to 28
    
     C test pointer is              11640096
     Fortran test pointer is           28
    

    Note: The program will leak the malloced bytes unless you add a free too.

    From PierU's comment:

    Fortran passes arguments by address: when you call roll_dice(test_ptr), this is not the content of test_ptr that is passed, but its address. That is, in terms of C a pointer to a pointer. Besides, for code correctness, test_p should be intent(inout) in your interface.