Allocate interoperability and C descriptors

You can still make it work, but to get hold of the non-interoperable Fortran type you’ll need to introduce a typeless “handle” object:

module somemodule_wrap
  
  use, intrinsic :: iso_c_binding, only: &
      c_double, c_int, c_ptr, &
      c_f_pointer, c_loc
  use somemodule, only: sometype, frout2
  implicit none

contains

  type(c_ptr) function sometype_create(len) bind(c)
    integer(c_int), value :: len
    type(sometype), pointer :: p
    allocate(p)
    allocate(p%a(len))
    sometype_create = c_loc(p)
  end function

  type(c_ptr) function sometype_a(ptr, n) bind(c)
    type(c_ptr), intent(in), value :: ptr
    integer(c_int), intent(out) :: n
    type(sometype), pointer :: p => null()

    call c_f_pointer(ptr,p)
    if (allocated(p%a)) then
      n = size(p%a)
      sometype_a = c_loc(p%a)
    else
      sometype_a = c_null_ptr
    end if

  end function

  subroutine sometype_free(ptr) bind(c)
    type(c_ptr), value :: ptr
    type(sometype), pointer :: p => null()
    call c_f_pointer(ptr,p)
    if (allocated(p%a)) deallocate(p%a)
    deallocate(p)
  end subroutine

  subroutine frout2_wrap(ptr) bind(c)
    type(c_ptr), value :: ptr
    type(sometype), pointer :: p => null()
    call c_f_pointer(ptr,p)
    if (allocated(p%a)) then
      call frout2(p)
    end if
  end subroutine


end module
// main.c
//
#include "somemodule_wrap.h"

int main(void) 
{
    void *h_sometype = sometype_create( 6 );

    int n;
    double *a;

    a = sometype_a(h_sometype, &n);
    for (int i = 0; i < n; i++) {
        a[i] = (double) i;
    }

    frout2_wrap(h_sometype);
    sometype_free(h_sometype);
    
    return 0;
}

Example build process and output:

ivan:~/fortran/somemodule$ make
gfortran -Wall -fcheck=all -std=f2018 -c somemodule.f90
gfortran -fc-prototypes -fsyntax-only somemodule_wrap.f90 > somemodule_wrap.h
gfortran -Wall -fcheck=all -std=f2018 -c somemodule_wrap.f90
gcc-10 -Wall -std=c11 -o main main.c somemodule_wrap.o somemodule.o -lgfortran
ivan:~/fortran/somemodule$ ./main
           1           6           6
   0.0000000000000000        1.0000000000000000        2.0000000000000000        3.0000000000000000        4.0000000000000000        5.0000000000000000  

gfortran is the only Fortran compiler I’m aware of that supports generation of the corresponding C prototypes.


Edit: here are the build files (both CMake and a custom Makefile) in case anyway is interested:

ivan:~/fortran/somemodule$ tree
.
├── CMakeLists.txt
├── Makefile
├── main.c
├── somemodule.f90
└── somemodule_wrap.f90

0 directories, 5 files
CMakeLists.txt
cmake_minimum_required(VERSION 3.12.0)

project(someproject VERSION 0.1.0 LANGUAGES C Fortran)

add_library(
    somemodule
    somemodule.f90
    somemodule_wrap.f90
)

add_custom_command(
    OUTPUT somemodule_wrap.h
    COMMAND gfortran -fc-prototypes -fsyntax-only ${CMAKE_CURRENT_SOURCE_DIR}/somemodule_wrap.f90 > ${CMAKE_CURRENT_SOURCE_DIR}/somemodule_wrap.h
    DEPENDS somemodule_wrap.f90
)

add_executable(main main.c somemodule_wrap.h)
target_link_libraries(main somemodule)
Makefile
FC=gfortran
CC=gcc-10

FCFLAGS=-Wall -fcheck=all -std=f2018
CFLAGS=-Wall -std=c11

LDFLAGS=-lgfortran

.phony: all clean

all: main

main: main.c somemodule_wrap.o somemodule.o
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

main.c: somemodule_wrap.h

somemodule_wrap.h: somemodule_wrap.f90
	gfortran -fc-prototypes -fsyntax-only $< > $@

somemodule_wrap.f90: somemodule.mod

somemodule_wrap.o somemodule_wrap.mod: somemodule_wrap.f90 somemodule.mod
	$(FC) $(FCFLAGS) -c $<

somemodule.o somemodule.mod: somemodule.f90
	$(FC) $(FCFLAGS) -c $<

clean:
	rm -rf *.o *.mod somemodule_wrap.h main
4 Likes