Question

I have tried to modify code from this this question to ISO_C_BINDING, but C_F_POINTER call results in Undefined pointer/array (VS2010 debugger) and print*, f_arr(i) triggers segmentation fault. I think I do follow closely instructions from the array interop.

main.c

extern void subr(int, float*);
int main(int argc, char **argv){
    int N = 3;
    int i;
    float data[3];
    for (i=0;i<N;i++) data[i]=i;
    subr(N,data);
}

sub.f90

subroutine subr(n, c_arr) BIND(C)
USE, INTRINSIC :: ISO_C_BINDING 
implicit none

INTEGER(C_INT),VALUE :: n
TYPE (C_PTR) :: c_arr
REAL(C_FLOAT), POINTER :: f_arr (:)
integer :: i,ml,mu

CALL C_F_POINTER (c_arr, f_arr, (/n/) )
ml = lbound(f_arr,1)
mu = ubound(f_arr,1)

do i=ml,mu
  print*, f_arr(i)
enddo
end subroutine

Do you know why I get failure in pointer conversion? (intel fortran compiler)

Was it helpful?

Solution

The example for calling Fortran from C via the ISO_C_BINDING in the Intel documentation is not very clear. But elsewhere, I have found this explanation: TYPE (C_PTR) :: X is equivalent to C's void **, i.e. pointer to void pointer. We need void *, a void pointer, and therefore must declare C_ARR with the VALUE attribute:

SUBROUTINE SUBR(N, C_ARR) BIND(C)
    USE, INTRINSIC :: ISO_C_BINDING 
    IMPLICIT NONE

    INTEGER (C_INT), VALUE :: N
    TYPE (C_PTR), VALUE :: C_ARR
    REAL (C_FLOAT), POINTER :: F_ARR(:)

    CALL C_F_POINTER(C_ARR, F_ARR, (/N/))                                     

    PRINT *, F_ARR

END SUBROUTINE

But I'm not sure that you need to call C_F_POINTER at all. The following works, too:

SUBROUTINE SUBR(N, ARR) BIND(C)
    USE, INTRINSIC :: ISO_C_BINDING 
    IMPLICIT NONE

    INTEGER (C_INT), VALUE :: N
    REAL (C_FLOAT) :: ARR(N)

    PRINT *, ARR

END SUBROUTINE

(Maybe it is good practice to use C_F_POINTER, I don't know. That's something the Intel documentation should show: When it is needed and when not.)

Edit: After looking a bit more into the interface in the Intel example, I think the C_F_POINTER call is needed there, because the array is part of a C struct. When declaring your TYPE, you cannot make the members arrays of a variable length, so you have to declare the data member as TYPE (C_PTR) and go through the C_F_POINTER routine.

So here is the same example for passing a pointer to a structure cleaned up:

SUBROUTINE SUBR_STRUCT(OBJ) BIND(C)
    USE, INTRINSIC :: ISO_C_BINDING 
    IMPLICIT NONE

    TYPE, BIND(C) :: VECTOR 
        INTEGER (C_INT) :: LEN
        TYPE (C_PTR) :: DATA
    END TYPE VECTOR

    TYPE (VECTOR), INTENT(IN) :: OBJ
    REAL (C_FLOAT), POINTER :: ARR(:)

    CALL C_F_POINTER (OBJ%DATA, ARR, (/OBJ%LEN/))

    PRINT *, ARR

END SUBROUTINE

This is called from C thus:

struct obj
{
    int len;
    float *data;
};

extern void subr_struct(const struct obj *);

int main(int argc, char **argv)
{
    float data[] = {0.12, 0.15, 0.18, 0.23, 0.29};
    struct obj o = {5, data};

    subr_struct(&o);

    return 0;
}

You can also pass the structure by value if you give OBJ the VALUE attribute.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top