Fortran 2000 provides a standardized mechanism for interoperating with C. Clearly, any entity involved must be such that equivalent declarations of it may be made in the two languages. This is enforced within the Fortran program by requiring all such entities to be 'interoperable'. We will explain in turn what this requires for types, variables, and procedures. They are all requirements on the syntax so that the compiler knows at compile time whether an entity is interoperable. We finish with two examples.
2. Interoperability of intrinsic typesThere is an intrinsic module called ISO_C_BINDING that contains named constants holding kind type parameter values for intrinsic types. Their names are shown in Table 1, together with the corresponding C types. The processor is not required to support all of them. Lack of support is indicated with a negative value.
Type | Named constant | C type or types |
INTEGER | C_INT | int, signed int |
C_SHORT | short int, signed short int | |
C_LONG | long int, signed long int | |
C_LONG_LONG | long long int, signed long long int | |
C_SIGNED_CHAR | signed char, unsigned char | |
C_SIZE_T | size_t | |
C_INT_LEAST8_T | int_least8_t | |
C_INT_LEAST16_T | int_least16_t | |
C_INT_LEAST32_T | int_least32_t | |
C_INT_LEAST64_T | int_least64_t | |
C_INT_FAST8_T | int_fast8_t | |
C_INT_FAST16_T | int_fast16_t | |
C_INT_FAST32_T | int_fast32_t | |
C_INT_FAST64_T | int_fast64_t | |
C_INTMAX_T | c intmax_t | |
REAL | C_FLOAT | float, float _Imaginary |
C_DOUBLE | double, double _Imaginary | |
COMPLEX | C_LONG_DOUBLE | long double, long double _Imaginary |
C_COMPLEX | _Complex | |
C_DOUBLE_COMPLEX | double _Complex | |
C_LONG_DOUBLE_COMPLEX | long double _Complex | |
LOGICAL | C_BOOL | _Bool |
CHARACTER | C_CHAR | char |
For character, interoperability also requires that the length type parameter be omitted or be specified by an initialization expression whose value is one. The following named constants (with the obvious meanings) are provided: C_NULL_CHAR, C_ALERT, C_BACKSPACE, C_FORM_FEED, C_NEW_LINE, C_CARRIAGE_RETURN, C_HORIZONTAL_TAB, C_VERTICAL_TAB.
3. Interoperability with C pointersFor interoperating with C pointers (which are just addresses), the module contains a derived type C_PTR that is interoperable with any C pointer type and a named constant C_NULL_PTR with the value NULL of C.
The module also contains the following procedures:
This is the mechanism for passing dynamic arrays between the languages. A Fortran pointer or assumed-shape array cannot be passed to C since its elements need not be contiguous in memory. However, an allocated allocatable array may be passed to C and an array allocated in C may be associated with a Fortran pointer.
4. Interoperability of derived typesFor a derived type to be interoperable, it must be given the BIND attribute explicitly:
TYPE, BIND(C) :: MYTYPE : END TYPE MYTYPE
Each component must have interoperable type and type parameters, must not be a pointer, and must not be allocatable. This allows Fortran and C types to correspond, for example
typedef struct { int m, n; float r; } myctype
is interoperable with
USE ISO_C_BINDING TYPE, BIND(C) :: MYFTYPE INTEGER(C_INT) :: I, J REAL(C_FLOAT) :: S END TYPE MYFTYPE
The name of the type and the names of the components are not significant for interoperability.
No Fortran type is interoperable with a C union type, struct type that contains a bit field, or struct type that contains a flexible array member.
5. Interoperability of variablesA scalar Fortran variable is interoperable if it is of interoperable type and type parameters, and is neither a pointer nor allocatable.
An array Fortran variable is interoperable if it is of interoperable type and type parameters, and is of explicit shape or assumed size. It interoperates with a C array of the same type types parameters and shape, but with reversal of subscripts. For example, a Fortran array declared as
INTEGER :: A(18, 3:7, *)
is interoperable with a C array declared as
int b[][5][18]6. Interoperability of procedures
A Fortran procedure is interoperable if it has an explicit interface and is declared with the BIND attribute:
FUNCTION FUNC(I, J, K, L, M), BIND(C)
All the dummy arguments must be interoperable. For a function, the result must be scalar and interoperable. The procedure has a 'binding label', which has global scope and is the name by which it is known to the C processor. By default, it is the lower-case version of the Fortran name. For example, the above function has the binding label 'func'. Another binding label may be specied:
FUNCTION FUNC(I, J, K, L, M), BIND(C, NAME='C_Func')
Such a procedure corresponds to a C function prototype with the same binding label. For a function, the result must be interoperable. For a subroutine, the prototype must have a void result.
7. Interoperability of global dataAn interoperable module variable or a common block with interoperable members may be given the BIND attribute:
USE ISO_C_BINDING INTEGER(C_INT), BIND(C) :: C_EXTERN INTEGER(C_LONG) :: C2 BIND(C, NAME='myVariable') :: C2 COMMON /COM/ R, S REAL(C_FLOAT) :: R, S BIND(C) :: /COM/
It has a binding label defined by the same rules as for procedures and interoperate with a C variable of a corresponding struct type.
8. Example of Fortran calling CC Function Prototype:
int C_Library_Function(void* sendbuf, int sendcount, int *recvcounts)
Fortran Module:
MODULE FTN_C INTERFACE INTEGER (C_INT) FUNCTION C_LIBRARY_FUNCTION & (SENDBUF, SENDCOUNT, RECVCOUNTS), & BIND(C, NAME='C_Library_Function') USE ISO_C_BINDING IMPLICIT NONE TYPE (C_PTR), VALUE :: SENDBUF INTEGER (C_INT), VALUE :: SENDCOUNT TYPE (C_PTR), VALUE :: RECVCOUNTS END FUNCTION C_LIBRARY_FUNCTION END INTERFACE END MODULE FTN_C
Fortran Calling Sequence:
USE ISO_C_BINDING, ONLY: C_INT, C_FLOAT, C_LOC USE FTN_C ... REAL (C_FLOAT), TARGET :: SEND(100) INTEGER (C_INT) :: SENDCOUNT INTEGER (C_INT), ALLOCATABLE, TARGET :: RECVCOUNTS(:) ... ALLOCATE( RECVCOUNTS(100) ) ... CALL C_LIBRARY_FUNCTION(C_LOC(SEND), SENDCOUNT, & C_LOC(RECVCOUNTS)) ...9. Example of C calling Fortran
Fortran Code:
SUBROUTINE SIMULATION(ALPHA, BETA, GAMMA, DELTA, ARRAYS), BIND(C) USE ISO_C_BINDING IMPLICIT NONE INTEGER (C_LONG), VALUE :: ALPHA REAL (C_DOUBLE), INTENT(INOUT) :: BETA INTEGER (C_LONG), INTENT(OUT) :: GAMMA REAL (C_DOUBLE),DIMENSION(*),INTENT(IN) :: DELTA TYPE, BIND(C) :: PASS INTEGER (C_INT) :: LENC, LENF TYPE (C_PTR) :: C, F END TYPE PASS TYPE (PASS), INTENT(INOUT) :: ARRAYS REAL (C_FLOAT), ALLOCATABLE, TARGET, SAVE :: ETA(:) REAL (C_FLOAT), POINTER :: C_ARRAY(:) ... ! Associate C_ARRAY with an array allocated in C CALL C_F_POINTER (ARRAYS%C, C_ARRAY, (/ARRAYS%LENC/) ) ... ! Allocate an array and make it available in C ARRAYS%LENF = 100 ALLOCATE (ETA(ARRAYS%LENF)) ARRAYS%F = C_LOC(ETA) ... END SUBROUTINE SIMULATION
C Struct Declaration
struct pass {int lenc, lenf; float* f, *c}
C Function Prototype:
void simulation(long alpha, double *beta, long *gamma, double delta[], struct pass *arrays)
C Calling Sequence:
simulation(alpha, &beta, &gamma, delta, &arrays);