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. (interoperate with derived type)
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 ?
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.
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
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.
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
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, 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
“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");
}