Example - Fortran calling C

When reading the manual “PGI User’s Guide” to find example demonstrating how can I call the C function in my Fortran code, I got the example 12.3 and also noticed that paragraph above it.

It states “There are other solutions to calling C from Fortran … For example, you can use the iso_c_binding intrinsic module…”. I got the example using iso_c_binding on the forum, too. (https://forums.developer.nvidia.com/t/interoperate-with-derived-type/131573/1)

However, it’s much more complicated and pgf90 started to support iso_c_binding module since version 8.0.

I wonder that is there any benifit using iso_c_binding module in such an simple example 12.3 ?

Thanks a lot. :)

Hi cyFeng,

If you have access to both the Fortran and C code and the code is fairly simple, then using ISO_C_BINDING is not necessary (though you might want to get in the habit of using it). However, if you don’t have access to the C code, like when calling a C library routine, ISO_C_BINDING can be a great help.

  • Mat

Thank you, Mat.
I tried to practice using iso_c_binding and made a simple case in which an array was assigned in C function and I wanted to use it in the Fortran code. However, I can’t get the correct array… I got the result: 1.5878655…E-314
How should I modify the Fortran code? Thank you.

testc.c

#include <stdio.h> 
void testc(double *pa)
{
 double a[5]={1.23, 2.46, 3.69, 4.11, 7.21}, b;
 int m;
 pa=a;
 for (m=0;m<5;m++,pa++)
 {
   b=*pa;
   b=b+1.0;
   *pa=b;
 }
}

testf.f90

program test
 use iso_c_binding 
 implicit none
 interface 
  subroutine testc(pa) bind(c) 
   use iso_c_binding 
   type(c_ptr):: pa
  end subroutine testc 
 end interface 
 
 type(c_ptr) :: pa
 real(c_double),pointer::fpa(:)
 
 call testc(pa)
 call c_f_pointer(pa, fpa, [5])
 print*, fpa(1)
 
end program test

Hi cyFeng,

There’s a few problems with your C code. First, by default Fortran passes by reference, so you need to be passing “**pa” not “*pa”. Secondly, “a” is a local array being allocated on the stack who’s lifetime will end once the routine returns. You need to dynamically allocate “a” so that it can be used in the Fortran routine. Finally, you should not increment “pa” since you want it pointing to the starting address of “a”, not the end.

Here’s an example:

% cat tc1.c
#include <stdio.h>
#include <malloc.h>

void testc(double **pa)
{
 double b;
 double *a;
 int m;

 a = (double*) malloc(sizeof(double)*5);
 a[0]=1.23;
 a[1]=2.46;
 a[2]=3.69;
 a[3]=4.11;
 a[4]=7.21;
 *pa=a;
 for (m=0;m<5;m++)
 {
   b=a[m];
   b=b+1.0;
   a[m]=b;
 }
}
% pgcc -c tc1.c
% pgf90 test1.f90 tc1.o
test1.f90:
% a.out
    2.230000000000000

Hope this helps,
Mat

Ya! I do learn a lot!
Thank you, Mat. :)

By the way, I noticed that the number of arguments passed between Example 12.3 and Example 12.4 (on the document “PGI User’s Guide”) are different! Is there any key point I missed?
Example 12.3

logical*1 bool1
character letter1
integer*4 numint1, numint2
real numfloat1
double precision numdoub1
integer*2 numshor1
external f2c_func
call f2c_func_(bool1, letter1, numint1, numint2, numfloat1, numdoub1, numshor1)
write( *, "(L2, A2, I5, I5, F6.1, F6.1, I5)")
+ bool1, letter1, numint1, numint2, numfloat1,numdoub1, numshor1
end

Example 12.4

#define TRUE 0xff
#define FALSE 0
void f2c_func_( bool1, letter1, numint1, numint2, numfloat1,\
numdoub1, numshor1, len_letter1)
char *bool1, *letter1;
int *numint1, *numint2;
float *numfloat1;
double *numdoub1;
short *numshor1;
int len_letter1;
{
*bool1 = TRUE; *letter1 = 'v';
*numint1 = 11; *numint2 = -44;
*numfloat1 = 39.6 ;
*numdoub1 = 39.2;
*numshor1 = 981;
}

[/code]

When a subroutine (or function) is called from a Fortran routine, and one or more of the arguments are character variables, the lengths of the character variables are passed, as well.

If the callee is also in Fortran, the extra arguments are hidden. If the callee is in C, the hidden parameters need type declarations and become visible.

Thank you, Shamsundar. :)
By the way, is “the lengths of the character variables” necessary when the iso_c_binding module is used? Would you please provide me with some reference other than the PGI User’s Guide? I really want to get a whole picture and learn more about how can Fortran code call the C function accurately.

Hi cyFeng,

By the way, is “the lengths of the character variables” necessary when the iso_c_binding module is used?

Yes. ISO_C_BINDING only describes single characters, not strings.

  • Mat

Hi, Mat,
I slightly modified the previous C code and F code which passing the c_ptr variable “b” by value. However, I got the following results… Did I do something stupid again :(

C code

#include <stdio.h>
#include <malloc.h>

void testc(double *pa) {
 double *a;
 int m;
 a = (double*) malloc(sizeof(double)*5);
 a[0]=1.23;
 a[1]=2.46;
 a[2]=3.69;
 a[3]=4.11;
 a[4]=7.21;
 pa=&a[0];
 for (m=0;m<5;m++,pa++)
 {
   printf("a[%d]= %f\n",m,a[m]);
   *pa=a[m];
   printf("Array assigned! *pa[%d]= %f\n", m, *pa);
 }
 pa=pa-m;
 printf("return to FORTRAN \n");
}

F code

program test
 use iso_c_binding 
 implicit none
 
 interface 
  subroutine testc(b) bind(c) 
   use iso_c_binding
   type(c_ptr), value ::b
  end subroutine testc 
 end interface 
 
 type(c_ptr):: b
 real(c_double), pointer :: a(:)
 integer::i
 
 call testc(b)
 call c_f_pointer(b, a,[5])
 print*,"Arrays", (a(i),i=1,5)

end program test

Result…
a[0]= 1.230000
Array assigned! *pa[0]= 1.230000
a[1]= 2.460000
Array assigned! *pa[1]= 2.460000
a[2]= 3.690000
Array assigned! *pa[2]= 3.690000
a[3]= 4.110000
Array assigned! *pa[3]= 4.110000
a[4]= 7.210000
Array assigned! *pa[4]= 7.210000
return to FORTRAN
Arrays 9.45656389865560588E-308 -1.02951151789266945E-086 -1.02951151789559317E-086 -5.16470253715498591E-284 3.94265074578772941E+234

Hi cyFeng,

“b” needs to be passed by reference in order to be used in the Fortran routine. This means changing “pa” to be a “double **” in the C code and adjusting accordingly.

program test
 use iso_c_binding
 implicit none

 interface
  subroutine testc(b) bind(c)
   use iso_c_binding
   type(c_ptr) :: b
  end subroutine testc
 end interface

 type(c_ptr):: b
 real(c_double), pointer :: a(:)
 integer::i

 call testc(b)
 call c_f_pointer(b, a,[5])
 print*,"Arrays", (a(i),i=1,5)

end program test



#include <stdio.h>
#include <malloc.h>

void testc(double **pa) {
 double *a;
 int m;
 a = (double*) malloc(sizeof(double)*5);
 a[0]=1.23;
 a[1]=2.46;
 a[2]=3.69;
 a[3]=4.11;
 a[4]=7.21;
 *pa=a;
 for (m=0;m<5;m++)
 {
   printf("a[%d]= %f\n",m,a[m]);
   (*pa)[m]=a[m];
   printf("Array assigned! *pa[%d]= %f\n", m, (*pa)[m]);
 }
 printf("return to FORTRAN \n");
}



% pgf90 test.f90 testc.c -g
test.f90:
testc.c:
% a.out
a[0]= 1.230000
Array assigned! *pa[0]= 1.230000
a[1]= 2.460000
Array assigned! *pa[1]= 2.460000
a[2]= 3.690000
Array assigned! *pa[2]= 3.690000
a[3]= 4.110000
Array assigned! *pa[3]= 4.110000
a[4]= 7.210000
Array assigned! *pa[4]= 7.210000
return to FORTRAN
 Arrays    1.230000000000000         2.460000000000000
    3.690000000000000         4.110000000000000         7.210000000000000

Hope this helps,
Mat

Hi Mat,
I do really appreciate for your help. :)