Home > Blockchain >  With iso_c_binding, sending c_ptr to C, malloc,, set value
With iso_c_binding, sending c_ptr to C, malloc,, set value

Time:11-30

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

CodePudding user response:

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.

  • Related