Passing both scalars and arrays (of any dimensions) from Fortran to C

别说谁变了你拦得住时间么 提交于 2021-02-07 19:14:27

问题


I have the following Fortran subroutine named show_value that calls a C function named show_value:

INTERFACE
    SUBROUTINE show_value(variable) BIND(C, name = "show_value")
        USE, INTRINSIC :: iso_c_binding
        TYPE(*) :: variable
    END SUBROUTINE
END INTERFACE

The C function show_value:

void show_value(const void *variable)
{
    printf("%d\n", *(int *) variable);
}

The Fortran subroutine works well when passing scalars to it. Example:

INTEGER :: x
x = 12
call show_value(x)

This will call the C function show_value and print 12, which is correct.

Now, according to the Fortran documentation, if one wants to enable subroutine show_value to receive also arrays (of any dimensions) and not just scalars, the line TYPE(*) :: variable should be changed to TYPE(*), DIMENSION(..) :: variable.

After making this change, when executing the following Fortran code:

INTEGER, DIMENSION(3) :: y
y(1) = 15
y(2) = 17
y(3) = 19
call show_value(y)

The C function show_value no longer prints the correct message (i.e. prints random numbers). Moreover, I found out that the address that the C function receives is 528 lower than the original (in Fortran). To confirm this:

void show_value(const void *variable)
{
    printf("%d\n", *(int *) (variable + 528));
}

... which prints 15 (correct number).

Any idea what is going here?

Environment: Ubuntu 14.04 64 bit, gfortran 4.9


回答1:


Although your first case, with scalar argument, works correctly matching with the void* argument, when the argument to the call is assumed-rank (which type(*), dimension(..) :: variable represents) the Fortran procedure is not interoperable with the C procedure with corresponding formal argument const void *variable.

Instead, it is necessary to use the CFI_cdesc_t mechanism:

#include <stdio.h>
#include <ISO_Fortran_binding.h>

void show_value(const CFI_cdesc_t* variable)
{
  printf("%d\n", *(int*) variable->base_addr);
}

You can find details of this in Fortran 2018 18.5.3.

Essentially, however, this is a descriptor which has much of the detail of the Fortran entity. Here, base_addr is the start of the data, but you'll also find allocatable/pointer/data status, rank, extents, type.

Alas, gfortran 4.9 doesn't support this. If it is supported at all, it will be in only very recent versions.


Alternatively, you may avoid using an assumed-rank assumed-type actual argument and pass instead the C-address of the argument using c_loc. Not quite so elegant, but more widely supported:

use, intrinsic :: iso_c_binding, only : c_loc, c_ptr, c_int
interface
  subroutine show_value(variable) bind(c)
    import c_ptr
    type(c_ptr), value :: variable
  end subroutine
end interface

integer(c_int), target :: x, y(3)

x = 12
y = [15, 17, 19]

call show_value(c_loc(x))
call show_value(c_loc(y))

end

This, however, leaves the problem of how the C function knows what to do with the argument.



来源:https://stackoverflow.com/questions/51268567/passing-both-scalars-and-arrays-of-any-dimensions-from-fortran-to-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!