0
votes

Suppose I have the following subroutine in Fortran


    subroutine exec(routine)

        implicit none

        external     :: routine
        real(kind=8) :: res

        call routine(2.0d0, res)
        print *, "Fortran Result: res = ", res

    end subroutine exec

This subroutine receives, as an argument, an external routine. Now, suppose that this routine is written in C, and that I need to call the Fortran routine exec from C as well. Just like this:


    void op(double x, double *f) {
        *f = pow(x, 2);
    }

    void main() {
        exec_(op);
    }

I know that, if instead of passing an external subroutine I was passing an integer, or a double, or another conventional type, it would work, but this code returns segmentation fault. Is there any way to pass a parameter of type external from C to Fortran?

1

1 Answers

0
votes

This works,you must use pointers:

void op(double *x, double *f) {
    *f = pow(*x, 2);
}

void main() {
    exec_(&op);
}

subroutine exec(routine)

    implicit none

    external     :: routine
    real(kind(1.d0)) :: res

    call routine(2.0d0, res)
    print *, "Fortran Result: res = ", res

end subroutine exec

Do not use kind=8 it does not declare 8 byte real, but a processor dependent type with kind number 8. Even the old and nonstandard real*8 would be better.

But it would be better to use Fortran 2003 c interoperability and the iso_c_binding module:

f.f90

subroutine exec(routine_c) bind(C,name="exec_") use iso_c_binding implicit none

    abstract interface
        subroutine sub(a,b) bind(C)
          use iso_c_binding
          implicit none
          real(c_double),value :: a
          real(c_double) :: b
        end subroutine
    end interface

type(c_funptr),value :: routine_c
real(c_double) :: res
procedure(sub),pointer :: routine

call c_f_procpointer(routine_c,routine)

call routine(2.0d0, res)
print *, "Fortran Result: res = ", res

end subroutine exec

c.c

#include <math.h>
void op(double x, double *f) {
    *f = pow(x, 2.0);
}

int main() {
    extern void exec_(void (*) (double, double *));
    exec_(&op);
    return 0;
}

compilation:

 gfortran c.c f.f90 -Wall -g -fbacktrace -fcheck=all
cc1: warning: command line option ‘-fbacktrace’ is valid for Fortran but not for C [enabled by default]
cc1: warning: command line option ‘-fcheck=all’ is valid for Fortran but not for C [enabled by default]
 ./a.out 
 Fortran Result: res =    4.0000000000000000