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?