问题
Hi I am using a Fortran 90 code to call a C function. Since I am manipulating addresses, the arguments of the C function should be properly matched in Fortran. I am using ifort and icc to compile the code and working on 64 bit machine.
Some testing showed that this will work also with int32_t
, although to prevent eventual pitfalls, I would like to keep the uint32_t
The C functions I am calling has the following prototypes
uint32_t encode_(uint32_t x, uint32_t y)
uint32_t decode_(uint32_t dec)
I can't call these functions simply by doing something like
integer :: cod,encode
cod = encode(i,j)
This will produce gibberish. Therefore I am using a workaround:
void code2d_(uint32_t j[] ){
uint32_t i;
i=encode_(j[0],j[1]);
// the underscore is due to the FORTRAN naming convention
printf("Coded %10d \n",i);
}
And subsequently in Fortran
integer :: cod,code2d
cod = code2d(i,j)
Well obviously I have some problem with the mismatch of the argument types. Unfortunately I don't know how to fix this. Since in my decode/encode functions binary address arithmetic is done it is quite important to preserve the uint32_t
.
回答1:
You appear to know about iso_c_binding, as you use the tag. Study the Fortran 2003 interoperability with C. Read the tag description and some documentation like http://gcc.gnu.org/onlinedocs/gcc-4.9.0/gfortran/Interoperability-with-C.html . There is no place in modern Fortran for your trailing underscores and similar stuff.
Fortran doesn't have any unsigned types, you have to use the signed. As long as the signed values are positive, it works. If you need larger values, use larger integer type. You can transfer()
the lower bytes to int32 if you need it.
Third, Fortran uses some variants of pass by reference by default, especially for bind(c)
procedures (it may be a reference to a copy or some other variant). You must use the value
attribute to pass by value.
uint32_t encode(uint32_t x, uint32_t y)
uint32_t decode(uint32_t dec)
module c_procs
interface
function encode(x, y) bind(C, name="encode")
use iso_c_binding
integer(c_int32_t) :: encode
integer(c_int32_t), value :: x, y
end function
function decode(x, y) bind(C, name="decode")
use iso_c_binding
integer(c_int32_t) :: decode
integer(c_int32_t), value :: dec
end function
end interface
end module
...
use iso_c_binding
use c_procs
integer(c_int32_t) :: cod, i, j
cod = encode(i,j)
Recent versions of GCC are able to detect that we are mixing signed and unsigned types during link-time optimizations:
rng.f90:173:0: warning: type of 'sub' does not match original declaration [-Wlto-type-mismatch]
ival = sub(jz, jsr)
^
rng_c.c:2:10: note: return value type mismatch
uint32_t sub(uint32_t a, uint32_t b) {
^
/usr/include/stdint.h:51:23: note: type 'uint32_t' should match type 'int'
typedef unsigned int uint32_t;
^
rng_c.c:2:10: note: 'sub' was previously declared here
uint32_t sub(uint32_t a, uint32_t b) {
You can ignore the warning or disable it if you know what you are doing.
回答2:
You could write a C function which takes a C_INT (or int in C) and then converts it to uint32_t. Then you link to this from Fortran.
in C:
uint32_t to_uint32_t ( int i ) {
return (uint32_t)i; //cast actually not needed since it implicitly converts
}
in fortran:
module convert
interface
integer(C_INT32_T) function integer_to_uint32_t_C ( i ) bind (c,name="to_uint32_t")
use iso_c_binding
use user32_constants_types
integer(c_int), value :: i
end function
end interface
end module convert
Then you can use integer_to_uint32_t_C
to pass Fortran integer values to C functions that expect the uint32_t
. Also, you might want to make a C function that converts in back to a plain ole int
so you can use the results in Fortran.
来源:https://stackoverflow.com/questions/24726446/how-to-call-a-c-function-in-fortran-and-properly-pass-uint32-t-arguments