How to write a C binding to call a Fortran subroutine with character argument z1*(*)?

@giraffe,

Please note if you share further details e.g., your programming background and strengths and interested and also convey a willingness to learn and quickly adopt modern Fortran facilities even for a short duration of getting this code with your employer to run on 64-bit platform before it’s all rewritten in another language, you will likely get further pointers that can really speed this all up for you and your employer and enable rapid migration away from Fortran.

Say you indicate your strengths and interests lie in C or C++, that might indicate one approach; or Python or Julia, some other.

For now let us presume your background and interests are in the C language. And now that you think iso_c_binding is interesting (by the way it’s BIND(C) that really matters, not the module), you might be further intrigued by “ISO_Fortran_binding.h”, the C header that enables the binding to Fortran on the C side.

I’m surprised no one has pointed ISO_Fortran_binding.h out to you yet, perhaps it’s because of pending issues with gfortran implementation.

But with this, if someone whose expertise lies with C and is willing to do a bit of work on the C side of the code can make matter simpler on the Fortran side of the fence.

Take the modified version of the code in your original post: you will still be advised to

  1. CONTAIN it in a MODULE in Fortran,
  2. use BIND(C) and use interoperable types and kinds instead of the default ones in Fortran

And just for illustration purposes, assume the Fortran subprogram is to receive a string of length 20 and the Fortran subprogram the defines the dummy argument with some value, like so.

module m
   use, intrinsic :: iso_c_binding, only : c_char
contains
   subroutine test(s) bind(C, name="test_")
      ! Argument list
      character(kind=c_char,len=*), intent(inout) :: s
      print *, "In Fsub: len(s) = ", len(s), "; expected is 20"
      s = c_char_"Hello Fortran!"
   end subroutine
end module

So then a C program as shown below can be a caller for this Fortran subroutine:

#include <stdio.h>
#include "ISO_Fortran_binding.h"

extern void test_(CFI_cdesc_t *);

int main()
{
   enum SLEN { SLEN = 20 }; // some desired string length
   char s[SLEN] = "";
   CFI_CDESC_T(0) sdat; // employ a macro defined in Fortran binding header
   int i = CFI_establish((CFI_cdesc_t *)&sdat, s, CFI_attribute_other,
                          CFI_type_char, (size_t)SLEN, (CFI_rank_t)0, (CFI_index_t)0); 
   test_((CFI_cdesc_t *)&sdat);
   printf("%s\n", s);
   return 0;
}

So here, note how the C code sets up a char array of desired length, it sets up a C descriptor type for interoperation with Fortran, and consumes the Fortran subprogram.

You can see how the Fortran side is now far less verbose and more importantly, the Fortran subprogram has a dummy argument of assumed-length (character(len=*)) which is otherwise not permitted in interoperable procedures. Note this is made workable by specific instructions on the C side using the descriptors and also the CFI_establish function. These became available starting Fortran 2018.

Click to see program behavior

C:\Temp>cl /c /W3 /EHsc c.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30038.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.

c.c

C:\Temp>ifort /c /standard-semantics m.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.3.0 Build 20210609_000000
Copyright (C) 1985-2021 Intel Corporation. All rights reserved.

C:\Temp>link c.obj m.obj /subsystem:console /out:c.exe
Microsoft (R) Incremental Linker Version 14.29.30038.1
Copyright (C) Microsoft Corporation. All rights reserved.

C:\Temp>c.exe
In Fsub: len(s) = 20 ; expected is 20
Hello Fortran!