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

Interop C and Fortran problem

zmi007
Beginner
652 Views

I cannot figure out, why I am not able to pass len argument correctly into c function from fortran (gfortran + gcc handle this code without any obvious problem)

simple C function

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

int32_t test (char* key, size_t len)
{
  int32_t hash = 666;
  printf("c key %s\n", key);
  printf("c len %u\n", len); // expected 2
  printf("c hash = %u\n", hash);
  return hash;
}

Fortran program

!-----------------------------------------------------------------------
!Module interface_module
!-----------------------------------------------------------------------
module interface_module
   implicit none

   interface
      function test(key, length) result(hash) bind(c, name="test")
         use iso_c_binding
         character(kind=c_char),dimension(*) :: key
         integer(c_size_t), value :: length
         integer(c_int32_t) :: hash
      end function test
   end interface

   abstract interface
      function function_hash_template(key, length)
         use iso_c_binding
         character(kind=c_char),dimension(*) :: key
         integer(c_size_t), value :: length
         integer(c_int32_t) :: function_hash_template
      end function function_hash_template
   end interface

contains

   function test_wrap(text) result(hash)
      use iso_fortran_env
      implicit none
      character(len=*),intent(in) :: text
      integer(int32) :: hash
      procedure(function_hash_template), pointer :: fun
      fun => test
      hash = int(hash_wrap(text, fun), int32)
   end function test_wrap


   !-----------------------------------------------------------------------
   !Function
   !-----------------------------------------------------------------------
   function  hash_wrap(text, fun) result(hash)
      use iso_c_binding
      implicit none
      character (len=*), target, intent(in) :: text
      procedure(function_hash_template), pointer :: fun
      integer(c_int32_t) :: hash
      character(kind=c_char), dimension(len_trim(text)+1) :: text_c
      integer(c_size_t) :: length

      text_c = f_to_c_string(text) ! convert to c string for compatibility

      length = len_trim(text) + 1
      write(*,*) "hash_wrap, length = ", length
      hash = fun(text_c,length)

   end function hash_wrap

   pure function f_to_c_string (f_string) result (c_string)
      use, intrinsic :: iso_c_binding, only: c_char, c_null_char
      implicit none
      character(len=*), intent(in) :: f_string
      character(len=1,kind=c_char), dimension(len_trim(f_string)+1) :: c_string
      integer :: n, i
      n = len_trim(f_string)
      do i = 1, n
         c_string(i) = f_string(i:i)
      end do
      c_string(n + 1) = c_null_char

   end function f_to_c_string

end module interface_module


!-----------------------------------------------------------------------
!Main program test_main
!-----------------------------------------------------------------------
program    test_main
   use interface_module
   implicit none

   write(*,*) test_wrap("1")

end program test_main

Instead of expected len = 2 I am getting always some uninitialized random len value in c function

./test_size_t_ifort_f.x 
 hash_wrap, length =                      2
c key 1
c len 2977349904
c hash = 666
         666

ifort --version
ifort (IFORT) 16.0.2 20160204

 

0 Kudos
1 Solution
Steven_L_Intel1
Employee
652 Views

Hmm. I will take a look at this in the morning. At first glance I think the problem is you're missing bind(C) on function_hash_template. This will change the way length is passed.

View solution in original post

0 Kudos
6 Replies
Steven_L_Intel1
Employee
653 Views

Hmm. I will take a look at this in the morning. At first glance I think the problem is you're missing bind(C) on function_hash_template. This will change the way length is passed.

0 Kudos
zmi007
Beginner
652 Views

Thank you, Steve.

bind(c) solves it indeed, I was a bit confused by the "abstract interface" of the function.

 


 

0 Kudos
Steven_L_Intel1
Employee
652 Views

Glad to hear it, though your saying gcc/gfortran accepted this suggests a bug in gfortran. Actually, the original code would have "worked" in some earlier versions of ifort as we had a bug in how we interpreted the VALUE attribute for non-bind(C) routines. We made the correct (standard-conforming) behavior the default in version 16.

0 Kudos
TimP
Honored Contributor III
652 Views

my gcc does warn about the discrepancy between %u and size_t (when -Wall is set, same for Intel C, which also warns about %u and int32_t).  %zu and %d are accepted without warning, but the thing still hangs at run time when using gfortran.

0 Kudos
Steven_L_Intel1
Employee
652 Views

For reference, here's the main issue at hand.

The Fortran standard specifies different behavior for the VALUE attribute depending on whether the procedure being declared is "interoperable" (has BIND(C)) or not. Note that this is NOT our !DEC$ ATTRIBUTES VALUE extension.

For an interoperable procedure, VALUE means pass-by-value. The actual words in the standard are "interoperable with the corresponding formal parameter of the type" (and not of a pointer type.)

For a non-interoperable procedure, one without BIND(C), VALUE causes a writable, temporary copy of the argument to be passed by reference. So in zmi007's case, the call through the procedure pointer, defined with an interface that did not have BIND(C), caused the address of a copy of "length" to be passed. The C routine interpreted the address as the length itself, which was wrong. Adding BIND(C) to the procedure pointer's interface corrects this.

When ifort first implemented the F2003 C interoperability features we got this wrong, and always treated the Fortran standard VALUE attribute the same as !DEC$ ATTRIBUTES VALUE. In version 15 we added -assume std_value to specify the standard behavior for non-interoperable procedures, and in version 16 turned this on by default. Most of the time, VALUE is used in conjunction with BIND(C) so we didn't see a lot of issues with this change, but there were some.

0 Kudos
zmi007
Beginner
652 Views
Thnx Steve for explanation. Yes, I will try to start a talk about it in gfortran bugzilla. Tim, just ignore warnings, I tried to create minimal working example from the code and didn't manage to do it clean enough obviously. Originally, I use there int64, int32 integers and uint32_t in C and some collection of trafo/shifting routines for different integers kinds. My task is to implement hash functions library in fortran and I needed to compare fortran hashes with hashes from c functions in order to verify my implementation. That is the background of this code.
0 Kudos
Reply