3

I am calling a C routine from a FORTRAN 90 code. All works fine, but I am wondering why and how when I call the C routine with less parameters that I should the compiler does not complain. What is the compiler doing here? I am using a Cray compiler.

test.c

extern "C" void test_(double* x, double* y, double* z){
    // do some work
}

driver.F90

MODULE DRIVER

! declare arrays
 DOUBLE PRECISION, DIMENSION(dim, dim), INTENT(IN) :: x
 DOUBLE PRECISION, DIMENSION(dim, dim), INTENT(IN) :: y

! call C subroutine
 CALL test(x, y)

END MODULE DRIVER
Manolete
  • 3,431
  • 7
  • 54
  • 92
  • Probably test function pops an undefined value that your fortran did not pushed to the stack(or pushes null which could be zero). Example: push x,y,z pop z,y,x but did not do latest push so it is push x,y pop z(it was y),y(it was x),undefined x(no push/ null) – huseyin tugrul buyukisik Jul 17 '13 at 15:32
  • 2
    Print z in you C method and find out. You should always be sure to pass in the correct number parameters when invoking external methods as each compiler is different. It is either setting z to the default value (zero or null), or it will corrupt the stack and you will have some memory issue that may show themselves further down the line... – MoonKnight Jul 17 '13 at 15:34
  • 1
    I can't believe this was downvoted. – 1'' Jul 17 '13 at 15:56
  • 2
    The compiler isn't going to "complain" because the Fortran compiler doesn't know that you are calling the C routine incorrectly. You could easily get a runtime error (crash). If you used the Fortran ISO_C_Binding and correctly described the C routine with a Fortran interface, the Fortran compiler would detect the incorrect call and issue an error message at compile time. – M. S. B. Jul 17 '13 at 16:20

1 Answers1

8

Fortran is a lot different than C when it comes to function calls. When passing arguments to a C routine, the compiler must know the type of each argument, so that it can generate the appropriate calling sequence - either put the arguments on the stack in the correct order and with the correct padding or put them in the expected registers. C compilers usually gather this information when then compile the code if the callee is defined before the caller. In all other cases, a function declaration in the form of a prototype should be provided.

In Fortran all arguments are typically (with some exceptions) passed by address, which means that what actually gets passed to the callee is a set of memory pointers. Pointers look the same - they are always of the same type and hence passed the same way. Therefore a Fortran compiler can generate a function call without knowing what arguments the callee is actually expecting. This greatly simplifies the Fortran compilers but is then a source of myriads of possible errors, like calling a function with the wrong argument types or even with the wrong number of arguments, to name a few. Special programs, called linters (from the name of the C programs verification utility lint), usually have to be used in order to guarantee that no such errors are present. Modern Fortran compilers also try to be much stricter than the older ones and try their best to detect errors whenever possible.

Modern Fortran versions provide the INTERFACE construct that allows explicit declaration of function interfaces, very much similar to function prototypes in C. Module subroutines and functions get their interfaces generated automatically and made available to the callers that USE the module. When calling a subroutine/function with an explicit interface, the compiler is able to verify the validity of the call, i.e. it checks if the number of arguments and their types matches the one in the interface.

You should provide an interface for the external routine and then the compiler would be able to perform the checks. One usually uses the ISO_C_BINDING method in order to interface to C code:

INTERFACE
  SUBROUTINE test(x, y, z) BIND(C, NAME="test")
    USE, INTRINSIC :: ISO_C_BINDING
    REAL(KIND=C_DOUBLE), INTENT(...) :: x  ! Put the proper intents
    REAL(KIND=C_DOUBLE), INTENT(...) :: y
    REAL(KIND=C_DOUBLE), INTENT(...) :: z
  END SUBROUTINE test
END INTERFACE

With this interface in place, CALL test(x, y) would result in compile-time error because of argument count mismatch.

Hristo Iliev
  • 72,659
  • 12
  • 135
  • 186