Hi everyone,
Basically, I want to call a Fortran procedure from a C function.
Since the actual code would be quite “messy”, I reproduced the situation in a much readable format.
Fortran
! ------------------------------------------------------
! PURE FORTRAN CORE
! ------------------------------------------------------
module mod_internal_
implicit none
contains
function actualProc(f, n) result(res)
integer(kind = 4), intent(in) :: n
real(kind = 8), intent(in) :: f(n)
real(kind = 8) :: res(n)
integer :: i
do i = 1, n
res(i) = f(i) * n
enddo
end function
end module
module mod2
use mod_internal_
implicit none
private
public :: actualFctWrapper
contains
function actualFctWrapper(f, n) result(res)
integer(kind = 4), intent(in) :: n
real(kind = 8), intent(in) :: f(n)
real(kind = 8), allocatable, target :: res(:)
res = actualProc(f, n)
end function
end module
! ------------------------------------------------------
! FORTRAN-C interface
! ------------------------------------------------------
module mod_cintf
use iso_c_binding
implicit none
private :: evalFct_c_
procedure(fctintf), private, pointer :: fptr_int_ => null()
abstract interface
function fctintf(f, n) result(res)
integer(kind = 4), intent(in) :: n
real(kind = 8), intent(in) :: f(n)
real(kind = 8), allocatable, target :: res(:)
end function
end interface
interface
subroutine acquireEvalFctPtr(fct) bind(c, name="acquireEvalFctPtr")
import c_funptr
type(c_funptr), value :: fct
end subroutine
end interface
contains
subroutine evalFct_c_(f, n, res)
real(c_double), intent(in), target :: f
integer(c_int), intent(in) :: n
real(kind = 8), allocatable, target :: res_(:)
type(c_ptr), intent(inout) :: res
integer(kind = 4) :: n_, i
real(kind = 8), pointer :: f_(:)
n_ = int(n, kind=4)
call c_f_pointer(c_loc(f), f_, [n_])
res_ = fptr_int_(f_, n_)
print *, " Evaluation from within Fortran:"
do i = 1, n_
write(*, fmt='(5x, f12.5)', advance='no') res_(i)
enddo
print *
res = c_loc(res_)
end subroutine
subroutine acquireEvalFct(fct)
procedure(fctintf), pointer, intent(in) :: fct
fptr_int_ => fct
call acquireEvalFctPtr(c_funloc(evalFct_c_))
end subroutine
end module
! ------------------------------------------------------
! main
! ------------------------------------------------------
program test
use mod2
use mod_cintf
implicit none
call acquireEvalFct(actualFctWrapper)
end program test
C
#include <stdio.h>
void (*__fptr)(double**, int*, double**);
void acquireEvalFctPtr(void (*fct)(double**, int*, double**)) {
double _rv[5] = {1.f, 2.f, 5.f, 4.f, 5.f};
int _ival = 2;
double *_res;
__fptr = fct;
// now call function pointer (implementation comes from Fortran)
__fptr((double **) &_rv, &_ival, &_res);
printf("\n Evaluation result is: \n");
for (unsigned i = 0; i < _ival; ++i)
printf(" %12.5f", _res[i]);
}
Issue
Now, the actual memory allocation happens in the Fortran procedure mod_internal_ actualProc().
This procedure is externally exposed via a wrapper actualFctWrapper(), which is there for setting up before actually calling it (in reality interfaces differ, that’s why there exist the wrapper, to ease/automate its call). In this latter procedure, the function result is an allocatable because it is deferred shape.
When calling the set function pointer in C __fptr((double **) &_rv, &_ival, &_res), this get to the Fortran_C interface procedure evalFct_c_(), which internally calls the internal function pointer which has been previously set to the actual (wrapper) evaluating function.
Now, the issue: how to free this allocated memory?
I put a free() at the end of the C code, but in the real code base I get an exception Invalid address specified to RtlValidateHeap( 000001C0F5B70000, 000001C0F76A7E50 ), while this does not happen in the example provided here. Cannot judge which one is the real faulty one…
Also, another concern I had is that res_ in evalFct_c_() should be implicitly deallocated at exit. So, I wonder if this might cause, at some later point some access violation or similar. Or that memory might be reused, overriding its older values, invalidating the use of the result _res from an ancient call from the C side __fptr((double **) &_rv, &_ival, &_res);.
Hope I was clear enough.
Thanks to everyone answering.