2

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)

0

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

4 Comments

You are right, there is no need to play with the pointer value at all and the shorter version is better (and valid). type(c_ptr) without value is actually void **
True, but I tried to use Arr(:) notation
@VladimirF: Yes, of course, void **. Silly typo, the compiler would have caught it. Thanks.
@Peter You cannot use (:) deferred or assumed shape for bind(C) procedure arguments. It is against the standard.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.