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

Callback using ISO_C_BINDING gives strange result without BIND(C)

Arjen_Markus
Honored Contributor I
362 Views

Hello, I am struggling a bit with Fortran callbacks that are passed to a C routine. In the course of various experiments with that I came across this problem:

C code:

/* printc.c --
       Print a value via a callback
*/
#include <stdio.h>
void print_via_cb( float x, void (*p)(float x) ) {

    fprintf(stderr,"Callback: %p\n", p );
    fprintf(stderr,"Callback: %f\n", x );
    (*p)(x);
}

Fortran code:

! testcall.f90 --
!     Test callbacks via ISO_C_BINDING
!
module mycallback
    implicit none
contains
subroutine printvalue( x )
    real, value :: x

    write(*,*) x
end subroutine printvalue
end module mycallback

program testcallback
    use mycallback
    use iso_c_binding
    implicit none

    real :: x

    interface
        subroutine print_via_cb( x, sub ) bind(c, name = 'print_via_cb' )
            use iso_c_binding
            real, value :: x
            interface
                subroutine sub(x)
                   real, value :: x
                end subroutine sub
            end interface
        end subroutine print_via_cb
    end interface

    x = 1.2
    call print_via_cb( x, printvalue )
end program testcallback

The output of this program was:

Callback: 000000013FEE1060
Callback: 1.200000

5.4472619E-39

rather than 1.20000.

If I add BIND(C) to the interface to the argument SUB, I get an error message, stating that the actual routine doesn't have that attribute and should have, but there is no warning or error if I leave it out (with the above result).

I am using Intel Fortran 15.0.1 at this moment.

0 Kudos
2 Replies
Steven_L_Intel1
Employee
362 Views

At the risk of sounding like a broken record ("What is this record thing you speak of?"), ISO_C_BINDING is just a module with some helpful declarations. Your program here doesn't use anything from it and the USE line could be removed without effect.

BIND(C) is critical, though. As posted here, the Fortran code won't compile, getting the error:

testcall.f90(26): error #8812: This procedure dummy argument to the BIND(C) procedure is not interoperable.  
                subroutine sub(x)
---------------------------^

You've declared print_via_cb as interoperable (that's what BIND(C) means), and that then requires that all dummy arguments be interoperable, which sub is not.

It is not sufficient to add BIND(C) to the interface of sub, you must also add it to the actual routine, printvalue. Why is this important? Well, leaving aside the fact that otherwise you have a mismatch in procedure characteristics, the VALUE attribute changes its meaning for interoperable procedures. If you don't say BIND(C), the Fortran standard VALUE attribute means "pass a writable anonymous copy by reference." If you DO say BIND(C), it means "pass by value".

We had this wrong for many years - starting with version 10 up through version 14 we treated the VALUE attribute the same as !DEC$ ATTRIBUTES VALUE. In version 14 we added /assume:std_value to get the standard behavior but had it off by default. In version 15 it was turned on by default.

If you add BIND(C) both to the interface for "sub" and to "printvalue", you get the results you want, since the C code is passing the argument by value.

0 Kudos
Arjen_Markus
Honored Contributor I
362 Views

Right, I was assuming more magic than is actually there. Well, struggling on with the actual problem now. Thanks for the clarification.

0 Kudos
Reply