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
- Large C library with 100+ API entry points.
- Almost all of them include string arguments (90% IN, some are OUT)
- This library was previously written in Fortran.
- Thousands of lines in 50-60 Fortran executables which call the C library assuming it is still Fortran.
- The C Library includes the necessary logic to "pretend" to be Fortran (supports pretending to be CVF, IVF, and gfortran)
- 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?