C interoperability with assumed-shape arrays

If you want to understand why name mangleing was such a big issue PRIOR to Fortran 2003 here are two examples (one (mine) is brute force, the second is more elegant)

Here is some code I wrote around 1998 to poll the OS environment to get the path to a home directory. Note the different naming conventions for a Cray T3E, an IBM SP (AIX) and Linux/Unix

/*
 homedir function
 
 FORTRAN callable C integer function that gets the home
 directory of the current user. Assumes $HOME has been
 set as a default environment variable by the system.
 homeidr returns the length of the string as its default
 value plus the directory name in the string array homeval.
 The maximum directory name length is 31 for now.

FORTRAN 90 INTERFACE

     Character(LEN=31) :: home_name
     Integer :: ret
     Integer, External :: homedir
!     
!  homedir is called from FORTRAN as follows
!
!
     ret=homedir(homename)

 NOTE: The extra int variable on the non-T3E C function interfaces
       below is a hidden variable that passes INTO C the
       length of the character string. The T3E uses macros/functions
       from fortran.h to do the correct conversion to/from C character
       pointers. Also note the different forms of the function names
*/

#include <ctype.h>
#include <stdlib.h>
#ifdef __T3E__
#include <fortran.h>
#include <string.h>
#endif
#define MAXSTR 31

char *getenv (const char *name);

#ifdef __T3E__
int HOMEDIR(_fcd homeval)
{
  int len2;
  char *homestr;
  const char *ht = "HOME";
  if ((homestr = getenv(ht)) == NULL)
  {
   printf("$HOME not set in environment\n");
   return(0);
  }
  len2=strlen(homestr);
  strncpy(_fcdtocp(homeval),homestr,len2);
  return(len2);
#else
#ifdef __AIX__
int homedir(char homeval[MAXSTR], int ftnlen)
#else
int homedir_(char homeval[MAXSTR], int ftnlen)
#endif
{
  int len2;
  char *homestr;
  const char *ht = "HOME";
  if ((homestr = getenv(ht)) == NULL)
  {
   printf("$HOME not set in environment\n");
   return(0);
  }
  len2=strlen(homestr);
  strncpy(homeval,(homestr),len2);
  ftnlen=len2;
  return len2;
#endif
}


The second example is a more elegant solution taken from the SANDIAs Exodus Fortran interfaces (still the default Fortran interfaces (For now - watch this space))

#ifdef ADDC_
#define F2C(name, NAME) name##_
#else
#ifdef _MSC_VER
#define F2C(name, NAME) NAME
#else
#define F2C(name, NAME) name
#endif
#endif

int F2C(excre, EXCRE)(char *path, int *clobmode, int *cpu_word_size, int *io_word_size, int *ierr, int pathlen
}

Note the hidden variable (pathlen) for the character string “path”

Now compare these with how Fortran 2003 simplified things by just allowing you to BIND to the actual C procedure name. So yeah those of us who actually had to deal with this way back when think it was a big deal. Since the current Exodus Fortran interfaces comprise over 150 subroutine calls it was VERY VERY big deal for the Exodus developers.

Now back to the OPs original topic. One of the motivating factors for the Fortran 2018 additions to C-Interop was a long standing issue with how MPI would react to situations where Fortran 90 decided to do a copy in (or copy out) most commonly seen with assumed shape arrays and passing array sections. A typical scenario would be something like this with a non-blocking receive

  1. You call an MPI function with a routine that is using assumed shape arrays. You don’t supply the compiler with an interface to the MPI function so the compiler has to decide if it wants to do pass by reference or copy in/out

  2. You call your routine wrapping the MPI function with an array section (not whole array argument) so the compiler decides it needs to do a copy in/out and creates a temporary array.

  3. The MPI non-blocking receive starts and immediatly returns to the calling function assuming that the communications are occuring in the background into a static memory space (static in the sense it will not disappear until the communication finishes)

  4. However, once the MPI function returns, the Fortan routine that called it is free to also return and DELETE the temp array before the communication is actually finished. So poor MPI is trying to write to memory that no longer exists.

This problem was not limited to just the non-blocking send/receives. Most MPI implementations had a variety of communication policies (rendevous, eager etc) you could select. If I remember correctly, in eager mode even blocking sends etc could send a short message WITHOUT buffering the data and return without waiting for a hand-shake from the receiver that the message has completed. This used to be (don’t think thats the case now) controlled by an environment variable that defined an “eager limit” message size which was the boundary between when MPI would buffer a message and when it would send it without buffering.

The moral of this story is for me the following. If you need to pass non-contiguous array sections to a C function or vice-versa, then the Fortran 2018 extensions are the way to go even if you have to write extra C code to do it. If you are only passing contiguous data and you can trust the compiler to do a pass by reference (a large leap of faith even today with some compilers) then writing an extra C wrapper to handle assumed shape arrays might be a waste of time

just my 2 cents

First, a correction: when it comes to the so-called enhanced interoperability with C in Fortran 2018. it is not about contiguous or non-contiguous array sections, rather it is about specific semantics in Fortran generally that do not map directly with C and which are otherwise not included in the interoperable list in the standard such as ALLOCATABLE, POINTER, etc. and also the topic of thread viz. assumed-shape array arguments.

Secondly, I saw @rwmsu’s earlier comments here as contradictory and possibly confusion-causing to other readers which then led to my query resulting in this sidebar that may probably be even more confusing to some!