C interoperability: Return void*

I’m trying to return an opaque type from a C-routine using void* in combination with c_ptr. This is discussed in Type(c_ptr) and void *, but not there is no solution for returning void* from C to use it in Fortran.

Below minimal example can be compiled with

gcc -c f.c;gfortran -c test.f90;gfortran test.o f.o;./a.out

or

icx -c f.c;ifx -c test.f90;ifx test.o f.o;./a.out

test.f90:

program return_void_star
  use ISO_C_binding
  implicit none

  interface
    subroutine f(v,ctx) bind(c)
      use ISO_C_binding
      integer, intent(in) :: v
      type(c_ptr), value :: ctx
      ! type(c_ptr), intent(out) :: ctx ! does not work either
    end subroutine f
  end interface

  type(c_ptr) :: pt
  integer, pointer :: my_ctx
  !allocate(my_ctx) ! does not help
  call f(42,pt)
  call c_f_pointer(pt,my_ctx)
  print*, my_ctx

end program return_void_star

f.c:

#include <stdlib.h>
#include <stdio.h>
void f(int *v, void *ctx){
  ctx = malloc(sizeof(int));
  ctx = v;
  printf("v %d\n",*v);
  printf("ctx %d\n",*(int*)ctx);
}

it gives the wrong result (0) with gcc and crashes with a segfault with Intel.

In the real use case, ctx should be a derived type, otherwise using an integer would be the obvious solution. Any help would be appreciated.

For this to work, your C routine has to accept a double pointer (void **) instead. Then, remove the value attribute of the pointer dummy argument in your interface. Currently, you pass the pointer argument as a copy but change the pointer address in the C routine. You can test it by initialising the pointer in Fortran with c_null_ptr and check the address after calling the interface. (At the moment, it should be still c_null_ptr afterwards).

1 Like

thanks, that was quick.

Here’s the solution for further references:

program return_void_star
  use ISO_C_binding
  implicit none

  interface
    subroutine f(v,ctx) bind(c)
      use ISO_C_binding
      integer, intent(in) :: v
      type(c_ptr) :: ctx
    end subroutine f
  end interface

  type(c_ptr) :: pt
  integer, pointer :: my_ctx
  call f(42,pt)
  call c_f_pointer(pt,my_ctx)
  print*, my_ctx

end program return_void_star
#include <stdio.h>
void f(int *v, void **ctx){
  *ctx = v;
  printf("v %d\n",*v);
  printf("ctx %d\n",**(int**)ctx);
}

Especially when allocating memory/returning a new object, another solution is via a function’s return argument, it makes it a bit easier to read and there is guaranteed interoperability between C and Fortran (I guess the return argument is by value, but standard experts will know that better):

ctx* get_context(int *v) { return (ctx*) v; }
interface
    type(c_ptr) function get_context(v) bind(C,name='get_context')
         import c_int,c_ptr
         integer(c_int), intent(inout) :: v
    end function
end interface

Note that int * typically means intent(inout) by reference; while const int *v typically means intent(in) by reference. if you set intent(in), the Fortran compiler may dangerously optimize the calls, with unintended consequences

1 Like