Pass arrays from C/C++ to Fortran and return a calculated array

Under the rules of current Fortran (Fortran 2008, but this is the same for when C interoperability was introduced in Fortran 2003), a Fortran procedure is not interoperable with C if it has an assumed shape dummy argument (other restrictions also apply). In your code degC, the dummy argument in the function DegCtoF, declared as

real(c_double), intent(in), dimension(:) :: degC

is such a thing.

So, under F2003 you cannot have such an interoperable function. Which is where things get tricky.

In the proposed draft for F2015 (based on the ISO TS29113 Further Interoperability of Fortran with C) such a thing is interoperable. And this syntax is (I think) supported by recent versions of gcc which is why the code is not rejected by gfortran.

(TS) Standardized interoperation with such a procedure with an assumed shape argument, however, requires using the C descriptor described in ISO_Fortran_binding.h on the C side which is not implemented in gcc. To do such interaction instead requires understanding the gcc array descriptor directly.

But you’re in luck. In your case you don’t really need to use an assumed shape dummy argument: you can use an explicit shape dummy argument and such interoperation is part of F2003. All you need to do is pass the size of the array.

Either way, an interoperable function must return a scalar result, so you’ll also want to move to a subroutine, as given in the answer by innoSPG.

Finally, I’ll mention your use of

real(c_double), bind(c) :: degF, degC

in the module.

These are interoperable global variables (through linkage association). You don’t reference these variables in the Fortran code: the dummy and the function result are not these things.


In this simple case from the above, and the other answer, one will happily have a subroutine like

subroutine DegCtoF(n, degC, degF) bind(c,name="DegCtoF")
  ...
end subroutine

but this is perhaps a good opportunity to describe the use of the C descriptor from ISO_Fortran_binding.h. Note, though, that in the immediate term gfortran does not support this approach.

Consider the Fortran source

subroutine DegCtoF(degC, degF) bind(c,name="DegCtoF")
   use, intrinsic :: iso_c_binding, only : c_double
   implicit none
   real(c_double), intent(in), dimension(:) :: degC
   real(c_double), intent(out), dimension(*) :: degF

   degF(1:SIZE(degC)) = degC*1.8+32
end subroutine DegCtoF

(for simplicity I’m going to assume that the memory management of degF is done all on the C side – naturally one could extend beyond the assumed size array). For this subroutine to be interoperable the argument corresponding to degC must be a pointer to CFI_cdesc_t.

Take the C code (with size magic numbers)

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

void DegCtoF(CFI_cdesc_t*, double*);

int main(int argc, char *argv[])
{
    printf("C and Fortran together!\n");

    CFI_CDESC_T(1) DegreesC_Fdesc;
    CFI_index_t extent[1] = {2};
    CFI_rank_t rank = 1;

    double DegreesC[2] = {32, 64};
    double DegreesF[2];

    CFI_establish((CFI_cdesc_t*)&DegreesC_Fdesc, &DegreesC, CFI_attribute_other, 
                  CFI_type_double, 2*sizeof(double), rank, extent);

    DegCtoF((CFI_cdesc_t*)&DegreesC_Fdesc, DegreesF);
    printf("%3.1f [C] = %3.1f [F]\n", DegreesC[0], DegreesF[0] );
    printf("%3.1f [C] = %3.1f [F]\n", DegreesC[1], DegreesF[1] );

    return 0;
}

Here CFI_establish establishes a suitable C descriptor DegreesC_Fdesc which can correspond to the assumed shape Fortran dummy argument. Inside the Fortran subroutine there is no problem at all assessing the size of the incoming array.

Leave a Comment