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.