NAG Fortran Compiler, Release 7.2: Advanced C interoperability

Navigation: Previous Up Next

## 11.8 Advanced C interoperability

- [7.0] The `C_FUNLOC` function from the intrinsic module `ISO_C_BINDING` accepts a non-interoperable procedure argument. The `C_FUNPTR` value produced should not be converted to a C function pointer, but may be converted to a suitable (also non-interoperable) Fortran procedure pointer with the `C_F_PROCPOINTER` subroutine from `ISO_C_BINDING`. For example,

             USE ISO_C_BINDING
             ABSTRACT INTERFACE
               SUBROUTINE my_callback_interface(arg)
                 CLASS(*) arg
               END SUBROUTINE
             END INTERFACE
             TYPE,BIND(C) :: mycallback
               TYPE(C_FUNPTR) :: callback
             END TYPE
             ...
             TYPE(mycallback) cb
             PROCEDURE(my_callback_interface),EXTERNAL :: sub
             cb%callback = C_FUNLOC(sub)
             ...
             PROCEDURE(my_callback_interface),POINTER :: pp
             CALL C_F_PROCPOINTER(cb%callback,pp)
             CALL pp(...)

  This functionality may be useful in a mixed-language program when the C_FUNPTR value is being stored in a data structure that is manipulated by C code.

- [7.0] The `C_LOC` function from the intrinsic module `ISO_C_BINDING` accepts an array of non-interoperable type, and the `C_F_POINTER` function accepts an array pointer of non-interoperable type. The array must still be non-polymorphic and contiguous.

  This improves interoperability with mixed-language C and Fortran programming, by letting the program pass an opaque “handle” for a non-interoperable array through a C routine or C data structure, and reconstruct the Fortran array pointer later. This kind of usage was previously only possible for scalars.

- [7.2] The named constant `C_PTRDIFF_T` has been added to the intrinsic module `ISO_C_BINDING`. This is the integer kind that corresponds to the C type `ptrdiff_t`, which is an integer large enough to hold the difference between two pointers. For example, the interface

              Interface
                Function diff_cptr(a,b) Bind(C)
                  Use Iso_C_Binding
                  Type(C_ptr),Value :: a, b
                  Integer(C_ptrdiff_t) diff_cptr
                End Function
              End Interface

  interoperates with the C function

              ptrdiff_t diff_cptr(void *a,void *b) {
                  return a - b;
              }

- [7.2] In the intrinsic module `ISO_C_BINDING`, the procedures `C_LOC` and `C_FUNLOC` are considered to be pure procedures, and `C_F_POINTER` and `C_F_PROCPOINTER` are considered to be impure. When it is used within a pure procedure, the argument of `C_FUNLOC` must also be a pure procedure.
- [7.1] Assumed-rank variables are permitted to be dummy arguments of a `BIND(C)` routine, even those with the `ALLOCATABLE` or `POINTER` attribute. An assumed-rank argument is passed by reference as a “C descriptor”; it is then up to the C routine to decode what that means. The C descriptor, along with several utility functions for manipulating it, is defined by the source file `ISO_Fortran_binding.h`; this can be found in the compiler's library directory (on Linux this is usually `/usr/local/lib/NAG_Fortran`, but that can be changed at installation time).

  This topic is highly complex, and beyond the scope of this document. The reader should direct their attention to the Fortran 2018 standard, or to a good textbook.

- [7.1] A `TYPE(*)` (“assumed type”) dummy argument is permitted in a `BIND(C)` procedure. It interoperates with a C argument declared as “`void *`”. There is no difference between scalar and assumed-size on the C side, but on the Fortran side, if the dummy argument is scalar the actual argument must also be scalar, and if the dummy argument is an array, the actual argument must also be an array.

  Because an actual argument can be passed directly to a `TYPE(*)` dummy, the `C_LOC` function is not required, and so there is no need for the `TARGET` attribute on the actual argument.

  For example,

        Program type_star_example
          Interface
            Function checksum(scalar,size) Bind(C)
              Use Iso_C_Binding
              Type(*) scalar
              Integer(C_int),Value :: size
              Integer(C_int) checksum
            End Function
          End Interface
          Type myvec3
            Double Precision v(3)
          End Type
          Type(myvec3) x
          Call Random_Number(x%v)
          Print *,checksum(x,Storage_Size(x)/8)
        End Program
        int checksum(void *a,int n)
        {
          int i;
          int res = 0;
          unsigned char *p = a;
          for (i=0; i<n; i++) res = 0x3fffffff&((res<<1) + p[i]);
          return res;
        }

- A `BIND(C)` procedure can have optional arguments. Such arguments cannot also have the `VALUE` attribute.

  An absent optional argument of a `BIND(C)` procedure is indicated by passing a null pointer argument.

  For example,

        Program optional_example
          Use Iso_C_Binding
          Interface
            Function f(a,b) Bind(C)
              Import
              Integer(C_int),Intent(In) :: a
              Integer(C_int),Intent(In),Optional :: b
              Integer(C_int) f
            End Function
          End Interface
          Integer(C_int) x,y
          x = f(3,14)
          y = f(23)
          Print *,x,y
        End Program

        int f(int *arg1,int *arg2)
        {
          int res = *arg1;
          if (arg2) res += *arg2;
          return res;
        }

  The second reference to `f` is missing the optional argument `b`, so a null pointer will be passed for it. This will result in the output:

       17 23