Search code examples
cfortranfortran-iso-c-bindinguint32

How to call a C function in Fortran and properly pass uint32_t arguments


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.


Solution

  • 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.