I feel like this should be an easy question, but I can't get it to work. I have some Fortran code that takes an input like:
      SUBROUTINE TRACE(X,Y,NAME,XX,YY)
      EXTERNAL NAME
      CALL NAME(X,Y,XX,YY)
and I'm trying to pass in a name from C++ in the form:
float x,y,xx,yy;
char * name="IGRF";
trace_(&x,&y,name,&xx,&yy);
It compiles, but I always get segfaults when I try to call the NAME subroutine. A subroutine called IGRF is defined in the file, and I can call the IGRF subroutine directly from C++, but need this TRACE routine. When running in gdb, it says the NAME variable comes through as a pointer to void.
I've tried passing NAME, &NAME, &NAME[0], a char NAME[4] that's stripped of its \0 to perfectly fit the name, and they all come back showing the same void pointer. Does anybody know how to get a function name from C++ into that EXTERNAL variable in Fortran?
Thank you
So one advantage of Fortran2003 and later is that C interoperability is defined into the standard; it's a bit of a PITA to use, but once it's done, it's guaranteed to work across platforms and compilers.
So here's cprogram.c, calling a Fortran routine getstring:
#include <stdio.h>
int main(int argc, char **argv) {
    int l;
    char *name="IGRF";
    l = getstring(name);
    printf("In C: l = %d\n",l);
    return 0;
}
and here's fortranroutine.f90:
integer(kind=c_int) function getstring(instr) bind(C,name='getstring') 
    use, intrinsic :: iso_c_binding
    character(kind=c_char), dimension(*), intent(IN) :: instr
    integer :: len
    integer :: i
    len=0
    do
       if (instr(len+1) == C_NULL_CHAR) exit
       len = len + 1
    end do
    print *, 'In Fortran:'
    print *, 'Got string: ', (instr(i),i=1,len)
    getstring = len
end function getstring
The makefile is simple enough:
CC=gcc
FC=gfortran
cprogram: cprogram.o fortranroutine.o
    $(CC) -o cprogram cprogram.o fortranroutine.o -lgfortran
fortranroutine.o: fortranroutine.f90
    $(FC) -c $^
clean:
    rm -f *.o cprogram *~
and running it works, under both gcc/gfortran and icc/ifort:
 In Fortran:
 Got string: IGRF
In C: l = 4
Update:  Oh, I just realized that what you're doing is rather more elaborate than just passing a string; you're essentially trying to pass a function pointer pointing to a C callback routine.    That's a little tricker, because you have to use Fortran interfaces to declare the C routine -- just using extern won't work (and isn't as good as explicit interfaces anyway, as there's no type checking, etc.)   So this should work:
cprogram.c:
#include <stdio.h>
/* fortran routine prototype*/
int getstring(char *name, int (*)(int));
int square(int i) {
    printf("In C called from Fortran:, ");
    printf("%d squared is %d!\n",i,i*i);
    return i*i;
}
int cube(int i) {
    printf("In C called from Fortran:, ");
    printf("%d cubed is %d!\n",i,i*i*i);
    return i*i*i;
}
int main(int argc, char **argv) {
    int l;
    char *name="IGRF";
    l = getstring(name, &square);
    printf("In C: l = %d\n",l);
    l = getstring(name, &cube);
    printf("In C: l = %d\n",l);
    return 0;
}
froutine.f90:
integer(kind=c_int) function getstring(str,func) bind(C,name='getstring')
    use, intrinsic :: iso_c_binding
    implicit none
    character(kind=c_char), dimension(*), intent(in) :: str
    type(c_funptr), value :: func
    integer :: length
    integer :: i
    ! prototype for the C function; take a c_int, return a c_int
    interface
        integer (kind=c_int) function croutine(inint) bind(C)
            use, intrinsic :: iso_c_binding
            implicit none
            integer(kind=c_int), value :: inint
        end function croutine
    end interface
    procedure(croutine), pointer :: cfun
    integer(kind=c_int) :: clen
    ! convert C to fortran procedure pointer,
    ! that matches the prototype called "croutine"
    call c_f_procpointer(func, cfun)
    ! find string length
    length=0
    do
       if (str(length+1) == C_NULL_CHAR) exit
       length = length + 1
    end do
    print *, 'In Fortran, got string: ', (str(i),i=1,length), '(',length,').'
    print *, 'In Fortran, calling C function and passing length'
    clen = length
    getstring = cfun(clen)
end function getstring
And the results:
$ gcc -g -Wall   -c -o cprogram.o cprogram.c
$ gfortran -c fortranroutine.f90 -g -Wall
$ gcc -o cprogram cprogram.o fortranroutine.o -lgfortran -g -Wall
$ gpc-f103n084-$ ./cprogram 
./cprogram 
 In Fortran, got string: IGRF(           4 ).
 In Fortran, calling C function and passing length
In C called from Fortran:, 4 squared is 16!
In C: l = 16
 In Fortran, got string: IGRF(           4 ).
 In Fortran, calling C function and passing length
In C called from Fortran:, 4 cubed is 64!
In C: l = 64
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With