Interoperability with C

John Reid, JKR Associates

1. Introduction

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 types

There 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.

Table 1. Interoperability between Fortran and C types
TypeNamed constantC type or types
INTEGERC_INTint, signed int
C_SHORTshort int, signed short int
C_LONGlong int, signed long int
C_LONG_LONGlong long int, signed long long int
C_SIGNED_CHARsigned char, unsigned char
C_SIZE_Tsize_t
C_INT_LEAST8_Tint_least8_t
C_INT_LEAST16_Tint_least16_t
C_INT_LEAST32_Tint_least32_t
C_INT_LEAST64_Tint_least64_t
C_INT_FAST8_Tint_fast8_t
C_INT_FAST16_Tint_fast16_t
C_INT_FAST32_Tint_fast32_t
C_INT_FAST64_Tint_fast64_t
C_INTMAX_T c intmax_t
REALC_FLOATfloat, float _Imaginary
C_DOUBLEdouble, double _Imaginary
COMPLEXC_LONG_DOUBLElong double, long double _Imaginary
C_COMPLEX_Complex
C_DOUBLE_COMPLEXdouble _Complex
C_LONG_DOUBLE_COMPLEX  long double _Complex
LOGICALC_BOOL_Bool
CHARACTERC_CHARchar

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 pointers

For 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 types

For 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 variables

A 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 data

An 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 C

C 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);

 


Valid XHTML 1.0! Comments on this or any other of the Group's pages should be sent by email to the FSG Web Editor.