Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

calling a c function from fortran

JacobB
New Contributor I
1,985 Views

Hello 

I have a c function which receives an array of doubles and make changes to it. 

for example:

on the "c" side 

void DoSomething (double **values);

 

on the "fortran" side I have 

real*8 values(*)

pointer(pvalues,values)

call DoSomething(pvalues)

 

in order to make the connection I create a module with and interface 

module f_calling_c

interface 

subroutine DoSomething(pvalues) Bind(C,Name="DoSomething")

USE , INTRINSIC :: ISO_BINDING

REAL (KIND=C_DOUBLE), pointer :: pvalues

end subroutine DoSomething 

end Interface

end module f_calling_c

 

when I compile the fortran code I get the following errors

error #6633 the type of the actual argument differs from the type of the dummy argument [pvalues]

error #7496 a non-pointer actual argument shall have a target attribute when associated with a pointer dummy argument [pvalues]

 

I know I am doing something wrong 

any help would really be appreciated 

best

jac

 

 

 

 

0 Kudos
6 Replies
Arjen_Markus
Honored Contributor II
1,956 Views

The problem is that Fortran pointers are not equivalent to C pointers. As you have a double indirection - double **values - the type on the Fortran side should be a generic one, type(*). By NOT specifying the attribute "value" on the Fortran side, you make sure that on the C side it can be seen as a void ** argument and that is what you need.

Caveat: I have not tested this ;).

If the argument on the C side had been double *values, then the Fortran side might have been:

real(c_double), dimension(*) :: values

or:

real(c_double), intent(inout) :: values 

(I have added the intent just to show you the ambiguity of C pointers)

Is the C function allocating the array of values? That would present different difficulties.

 

Before I continue speculating, could you explain what the C function actually does? There is ample ambiguity here and on the Fortran side you may need to be specific.

 

0 Kudos
JacobB
New Contributor I
1,892 Views

Hi 

thanks for your reply 

so I can't pass a pointer on the fortran side ?

yes on the C side, the function allocates memory for the passed pointer 

best

jac

 

0 Kudos
Arjen_Markus
Honored Contributor II
1,878 Views

You can pass C pointers to and from Fortran,  that is not the problem. The thing is that an argument of type "double *" in a C function could mean two things (maybe even more): it is the starting address of an array of double-precision numbers or it is the address of a scalar and the function will change its value. In your case the C function allocates an array and returns the address. If you need to deallocate the memory, then that should be done on the C side.

To use the array in Fortran, use code like:

call c_f_pointer( c_ptr, farray [size] )

The interface of the C function may be:

interface
subroutine DoSomething( allocated_ptr ) bind(C,name="DoSomething")
    use iso_c_binding, only: c_ptr
    type(c_ptr), intent(out) :: allocated_ptr
end subroutine DoSomething
end interface

Since Fortran passes arguments by reference (by default, that is), you have a double indirection as required in this way.

0 Kudos
JacobB
New Contributor I
1,849 Views

hi 

thanks a lot for your support . True that pointers in c/c++ can be ambiguous

I tried what you suggested 

module fci

interface
subroutine do_something (pvol,n) bind(C,NAME="do_something")
use iso_c_binding, only: c_ptr , c_int
type(c_ptr), intent(out) :: pvol
integer(KIND=C_INT), intent(in) :: n
end subroutine do_something
end interface

end module fci

 

the fortran function is as follows

use fci
pointer (ipvol, vol)
real*8 vol(*)
call do_something(pvol,10)

the module compiles fine but the main code does  not 

I get the following error 

error #6633: The type of the actual argument differs from the type of the dummy argument. [PVOL]

 

it seems that there is need to specify the type of the pointer but I do not know how 

best

jac

 

0 Kudos
Arjen_Markus
Honored Contributor II
1,837 Views

You are using a so-called Cray pointer - an extension that has been superseded by the pointers that were introduced in the Fortran 90 standard. What you should do instead is something along these lines:

use fci
use iso_c_binding
type(c_ptr) :: pvol
real(kind=kind(1.0d0)), pointer :: vol(:)
call do_something(pvol,10)
call c_f_pointer(pvol, vol, [10])

Also: the declaration of n in the interface is probably best done as:

integer(kind=c_int), value :: n

as the C function likely uses "int n" - which is a value, not a reference to an intent(in) variable.

Anyway, I took your code and the above fragment and created a working program ;).

And instead of REAL*8 I use the KIND= attribute, that is standard, whereas REAL*8 is a very common extension but an extension.

 

0 Kudos
JacobB
New Contributor I
1,828 Views

Hi

Now I got it 

thank you very much 

jac

 

0 Kudos
Reply