Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.0k views
in Technique[技术] by (71.8m points)

arrays - C and Fortran interoperability for strings

I am trying to call some Fortran code from C but I didn't find the proper way of passing a C char array.

   SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
     USE ISO_C_BINDING
     IMPLICIT NONE
     CHARACTER*(C_CHAR)      c_message
     CHARACTER*(256)         f_message
     CALL C_F_POINTER( C_LOC(c_message), f_message)
     WRITE(*,*) f_message,LEN(f_message)
   END

The BIND(C) force the parameter c_message to be size 1. How can I access to other elements of the c_message string?

Compiler : GCC 4.8.2

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I am trying to call some Fortran77 code from C but I didn't find the proper way of passing a C char array.

All Fortran 77 implementations I've ever used have afforded implementation-specific mechanisms for interoperation with C. Generally these involve the C side knowing the name-mangling and argument-passing conventions employed by the Fortran compiler, and using them at the interface. The GCC system's conventions are not that hard to use.

You seem to be interested in the Fortran/C interop facility introduced in Fortran 2003, however. Supposing that your Fortran 77 code also conforms with Fortran 2003 (or can be made to do so), it should be possible to write an interoperable wrapper in Fortran 2003. Be aware, however, that the C interop facility does not provide (directly) for interoperability of Fortran variables or subprogram parameters of type character with length greater than 1 or kind different from c_char. On the other hand, keep also in mind that the length of a Fortran character object is not the same thing as the dimension(s) of a character array.

You have a couple of interoperable alternatives for providing a C-facing interface by which to accept a C char array. Perhaps the clearest one would be to accept a Fortran assumed-size character array:

SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
    USE ISO_C_BINDING
    IMPLICIT NONE
    CHARACTER(kind=C_CHAR), dimension(*), intent(in) :: c_message
    ! ...
END

The most likely alternative is to accept C's array pointer directly:

SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
    USE ISO_C_BINDING
    IMPLICIT NONE
    type(C_PTR), value :: c_message
    ! ...
END

The latter is necessary if the C-side array pointer might be null. Both require an explicit length to be passed as well if the array cannot be relied upon to be null terminated.

In any event, if you ultimately want a Fortran character variable having length greater than 1 (as opposed to an array having dimension greater than 1), then the interoperable interfaces can't provide that directly -- such types are not among those to which the C interop provisions are applicable. Unless you can rely on the default character kind to be c_char, you'll need to couple them with copy-in(/ copy-out) in order to convert the characters between kinds. With the former interface, it should be obvious how you could copy the array to a Fortran scalar character having length greater than 1. For the pointer variant, it might be useful to use a conversion function something like this:

subroutine C_string_ptr_to_F_string(C_string, F_string)
    use ISO_C_BINDING
    type(C_PTR), intent(in) :: C_string
    character(len=*), intent(out) :: F_string
    character(len=1, kind=C_CHAR), dimension(:), pointer :: p_chars
    integer :: i
    if (.not. C_associated(C_string)) then
      F_string = ' '
    else
      call C_F_pointer(C_string, p_chars, [huge(0)])
      do i = 1, len(F_string)
        if (p_chars(i) == C_NULL_CHAR) exit
        F_string(i:i) = p_chars(i)
      end do
      if (i <= len(F_string)) F_string(i:) = ' '
    end if
end subroutine

(Derived from the Fortran Wiki's C_interface_module's C_F_string_ptr subroutine)

On the other hand, if you can (or anyway do) rely on the default character kind to be c_char then you have an additional alternative. You can usefully cause a character array such as the parameter in the first example to be associated with a scalar character object of default kind and length greater than one. In particular, if a dummy argument of the wrapped function is a scalar character with assumed length, or with fixed length not exceeding the number of array elements, then you can rely on argument association to associate it with the character array in the wrapper. In other words, in that case you can just pass the array as the actual argument.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...