Quantcast
Channel: Intel® Fortran Compiler
Viewing all articles
Browse latest Browse all 3270

Headache free C-Fortran interface under 11+

$
0
0

I'm in charge of a rather large C-Fortran interface for our codes that has recently run afoul of the extra strictness included in 11+.

"error #8532: A character dummy argument with length other than 1 is not interoperable"

This problem has previously been raised by a coworker in 2011. Unfortunately, the answer given does not solve the real problem at hand (mostly because the co-worker was just trying to solve one specific issue and not the entire issue). I also understand that the old solution operated in a gray area, however, the implementation met all of the requirements imposed by operation in this gray area.

Problem Description

  1. Large C library with 100+ API entry points.
  2. Almost all of them include string arguments (90% IN, some are OUT)
  3. This library was previously written in Fortran.
  4. Thousands of lines in 50-60 Fortran executables which call the C library assuming it is still Fortran.
  5. The C Library includes the necessary logic to "pretend" to be Fortran (supports pretending to be CVF, IVF, and gfortran)
  6. The C Library provides an optional auto-generated module with the interface/subroutine/BIND magic to give argument type/dimension checking to these codes.

Example C API method

/* "data" is scalar to 7D, any of the primitive fortran types */
ABC_API(void) foobar (
  IN int *id,
  IN const char *folder FDECL_LENGTH_MIXED(folder),
  IN const char *file FDECL_LENGTH_MIXED(file),
  IN_BUFFER_ANY const void *data,
  IN_ESIZE(10) const int descriptor[],
  IN int *count,
  IN_ESIZE_MAX(7) const int startingElement[],
  OUT_ESIZE(2) int status[]
  FDECL_LENGTH_AFTER(folder)
  FDECL_LENGTH_AFTER(file)
);

Example Fortran Module entry for that routine (snipped for brevity)

! toplevel foobar selects type/dimension call
interface foobar
    module procedure foobar_i0, foobar_i1, foobar_i2, foobar_i3, foobar_i4, foobar_i5, foobar_i6, foobar_i7 &
          , foobar_r0, foobar_r1, foobar_r2, foobar_r3, foobar_r4, foobar_r5, foobar_r6, foobar_r7 &
          , foobar_d0, foobar_d1, foobar_d2, foobar_d3, foobar_d4, foobar_d5, foobar_d6, foobar_d7 &
          , foobar_cx0, foobar_cx1, foobar_cx2, foobar_cx3, foobar_cx4, foobar_cx5, foobar_cx6, foobar_cx7 &
          , foobar_l0, foobar_l1, foobar_l2, foobar_l3, foobar_l4, foobar_l5, foobar_l6, foobar_l7 &
          , foobar_c0, foobar_c1, foobar_c2, foobar_c3, foobar_c4, foobar_c5, foobar_c6, foobar_c7
endinterface foobar

! top level generic interface selects one of the following, which calls the C binding routines
subroutine foobar_i0 ( id, folder, file, idata, dsdes, ne, selem, istat )
    implicit none
    integer, intent(in) :: id
    character(len=*), intent(in) :: folder
    character(len=*), intent(in) :: file
    integer, intent(in) :: idata
    integer, intent(in) :: dsdes(DSDES_COUNT)
    integer, intent(in) :: ne
    integer, intent(in) :: selem(*)
    integer, intent(out) :: istat(STAT_COUNT)
    call foobar_internal_i0(id, folder, file, idata, dsdes, ne, selem, istat)
endsubroutine foobar_i0
subroutine foobar_i1 ( id, folder, file, idata, dsdes, ne, selem, istat )
    implicit none
    integer, intent(in) :: id
    character(len=*), intent(in) :: folder
    character(len=*), intent(in) :: file
    integer, intent(in) :: idata(:)
    integer, intent(in) :: dsdes(DSDES_COUNT)
    integer, intent(in) :: ne
    integer, intent(in) :: selem(*)
    integer, intent(out) :: istat(STAT_COUNT)
    call foobar_internal_i(id, folder, file, idata, dsdes, ne, selem, istat)
endsubroutine foobar_i1
! repeat subroutine foobar_XYZ for each type and dimension
! These _internal_ variants are the methods actually bound to the C, which provide both Scalar and
! DIMENSION(*) versions so that C just gets a raw pointer

subroutine foobar_internal_c ( id, folder, file, cdata, dsdes, ne, selem, istat ) bind(C, name='FOOBAR')
    implicit none
    integer, intent(in) :: id
    character(len=*), intent(in) :: folder
    character(len=*), intent(in) :: file
    character(len=*), intent(in) :: cdata(*)
    integer, intent(in) :: dsdes(DSDES_COUNT)
    integer, intent(in) :: ne
    integer, intent(in) :: selem(*)
    integer, intent(out) :: istat(STAT_COUNT)
endsubroutine foobar_internal_c

subroutine foobar_internal_c0 ( id, folder, file, cdata, dsdes, ne, selem, istat ) bind(C, name='FOOBAR')
    implicit none
    integer, intent(in) :: id
    character(len=*), intent(in) :: folder
    character(len=*), intent(in) :: file
    character(len=*), intent(in) :: cdata
    integer, intent(in) :: dsdes(DSDES_COUNT)
    integer, intent(in) :: ne
    integer, intent(in) :: selem(*)
    integer, intent(out) :: istat(STAT_COUNT)
endsubroutine foobar_internal_c0
! repeat foobar_internal_XYZ for each type

Basically, I can't update the C code (and its API) in any manner which would break the expectation that it is "Fortran". Now I'm caught because 11+ no longer allows this behavior. I also cannot introduce any required shim code on the Fortran side, because the API must work with or without the auto-generated module.

I'm more than happy to maintain the headache of the C side understanding Fortran string-isms, etc. This is far easier than introducing C-isms to the Fortran side, in both code support AND time-burden to update/verify all of the production Fortran.

Is there a way I can update this interface to meet both my desire to: (a) provide a type checking solution for the library, and (b) not introduce any required shimming?

 


Viewing all articles
Browse latest Browse all 3270

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>