Pass arrays from C / C ++ to Fortran and return the computed array

I am trying to pass an array from C / C ++ to a Fortran 2003 module and get the calculated values ​​back to C / C ++. I was able to pass and return single values ​​(scalars) just fine, but getting the array back and forth turned out to be difficult. I found many threads by scalar values, and I managed to do these jobs.

I modeled my array-based functions after my working scalar functions.

I am using gcc / gfortran.

Here is the Fortran module (ConvertUnitsLib.f03).

module ConvertUnitsLib use :: iso_c_binding ! for C/C++ interop real(c_double), bind(c) :: degF, degC public DegCtoF contains ! ! Convert temperature degrees Celsius Fahrenheit ! real(kind = c_double) function DegCtoF(degC) result(degF) & & bind(c, name = "DegCtoF") real(c_double), intent(in), dimension(:) :: degC real(c_double), dimension(size(degC)) :: degF do i = 1, size(degC) degF(i) = ( degC(i) * 1.8 ) + 32 end do end function DegCtoF ! End of module end module ConvertUnitsLib 

And C / C ++, (CFort.cpp)

 #include <stdio.h> #ifdef __cplusplus extern"C" { #endif double DegCtoF(double *[]); #ifdef __cplusplus } #endif /**********************************************************************/ int main(int argc, char *argv[]) { printf("C/C++ and Fortran together!\n"); double DegreesC[2] = {32, 64}; double DegreesF[2]; DegreesF = DegCtoF(&DegreesC); printf("%3.1f [C] = %3.1f [F]\n", DegreesC, DegreesF ); return 0; } 

Last but not least: Makefile

 # C++ directives CC=g++ CFLAGS=-std=c++11 # Fortran directives FC=gfortran FFLAGS=-std=f2003 all: clean $(FC) $(FFLAGS) -c -fcheck=all ConvertUnitsLib.f03 $(CC) $(CFLAGS) -c CFort.cpp $(FC) $(FFLAGS) ConvertUnitsLib.o CFort.o -o convert clean: rm -f *.o rm -f *.mod 
+5
source share
2 answers

Before francescalus confirms this, I was going to say that from what I know was a little old, compatibility does not allow you to do what you are trying to do with arrays. In addition, some good habits are always crucial when coding. For example, using implicit none in fortran to force all variables to be declared before they are used. Using a named constant when the language allows it, for example, 2 , which you use as the size of the array in fortran.

Below is a modified version of your code that should do something like what you want to achieve.

// Fortran

 module ConvertUnitsLib use :: iso_c_binding ! for C/C++ interop !real(c_double), bind(c) :: degF, degC implicit none public DegCtoF contains ! ! Convert temperature degrees Celsius Fahrenheit ! subroutine DegCtoF(degC, degF, n)& bind(c, name = "DegCtoF") integer, intent(in) :: n real(c_double), intent(in), dimension(n) :: degC real(c_double), intent(out), dimension(n) :: degF integer :: i do i = 1, n degF(i) = ( degC(i) * 1.8 ) + 32 end do end subroutine DegCtoF 

// C ++

 #include <stdio.h> #ifdef __cplusplus extern"C" { #endif double DegCtoF(double [], double [], const int *); #ifdef __cplusplus } #endif /**********************************************************************/ int main(int argc, char *argv[]) { const int N = 2; printf("C/C++ and Fortran together!\n"); double DegreesC[N] = {32, 64}; double DegreesF[N]; DegCtoF(DegreesC, DegreesF, &N); for(int i = 0; i<N; i++){ printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i] ); } return 0; } 
+5
source

According to the rules of the current Fortran (Fortran 2008, but this is the same for when C-compatibility was introduced in Fortran 2003), the Fortran procedure is not C-compatible if it has the supposed argument of a fictitious figure (other restrictions apply). In your degC code degC dummy argument to the DegCtoF function declared as

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

- such a thing.

So, in F2003 you cannot have such interoperability. This is where things get tricky.

In the proposed project for F2015 (based on ISO TS29113 Further Fortran compatibility with C ), such a thing is compatible. And this syntax (I think) is supported by the latest versions of gcc, so the code is not rejected by gfortran.

(TS) The standard interaction with such a procedure with an assumed form argument requires, however, the use of the C descriptor described in ISO_Fortran_binding.h on the C side, which is not implemented in gcc. Such interaction instead requires understanding the gcc array descriptor directly.

But you are lucky. In your case, you do not need to use the argument of the alleged figure: you can use the explicit argument of the dummy figure, and this interaction is part of F2003. All you have to do is pass the size of the array.

In any case, the compatible function should return a scalar result, so you also need to go to the subroutine, as indicated in innoSPG's answer.

Finally, I mentioned your use

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

in the module.

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


In this simple case, from the above and in another answer, you will 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 . Please note, however, that in the near future gfortran does not support this approach.

Consider the source of Fortran

 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 assume that degF memory degF is done on the C side - naturally, you can go beyond the intended size array). For this routine to be interoperable, the argument corresponding to degC must be a pointer to CFI_cdesc_t .

Take the C code (with magic size 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 sets a suitable C DegreesC_Fdesc descriptor, which may correspond to the intended argument of the Fortran argument. Inside the Fortran routine, there is no problem evaluating the size of the incoming array.

+5
source

Source: https://habr.com/ru/post/1239644/


All Articles