Creating a FORTRAN interface to a C function that returns a char*

后端 未结 7 1373
天涯浪人
天涯浪人 2020-12-01 10:11

I\'ve been held up on this for about a week, now, and have searched forum after forum for a clear explanation of how to send a char* from C to FORTRAN. To make the matter m

7条回答
  •  一个人的身影
    2020-12-01 10:24

    Strings of dynamic length are always a bit tricky with the C interaction. A possible solution is to use pointers.

    First a simple case, where you have to hand over a null-character terminated string to a C-Function. If you really pass the string only in, you have to ensure to finalize it with the c_null_char, thus this direction is pretty straight forward. Here are examples from a LuaFortran Interface:

    subroutine flu_getfield(L, index, k)
      type(flu_State)  :: L
      integer          :: index
      character(len=*) :: k
    
      integer(kind=c_int) :: c_index
      character(len=len_trim(k)+1) :: c_k
    
      c_k = trim(k) // c_null_char
      c_index = index
      call lua_getfield(L%state, c_index, c_k)
    end subroutine flu_getfield
    

    And the interface of lua_getfield looks like:

    subroutine lua_getfield(L, index, k) bind(c, name="lua_getfield")
      use, intrinsic :: iso_c_binding
      type(c_ptr), value :: L
      integer(kind=c_int), value :: index
      character(kind=c_char), dimension(*) :: k
    end subroutine lua_getfield
    

    And the C-Code interface is:

    void lua_getfield (lua_State *L, int idx, const char *k)
    

    Now the little more complex case, where we have to deal with a returned string from C with a dynamic length. The most portable solution I found so far is using pointers. Here is an example with a pointer, where the string is given by the C-Routine (also from the Aotus library mentioned above):

    function flu_tolstring(L, index, len) result(string)
      type(flu_State) :: L
      integer :: index
      integer :: len
      character,pointer,dimension(:) :: string
    
      integer :: string_shape(1)
      integer(kind=c_int) :: c_index
      integer(kind=c_size_t) :: c_len
      type(c_ptr) :: c_string
    
      c_index = index
      c_string = lua_tolstring(L%state, c_index, c_len)
      len = int(c_len,kind=kind(len))
      string_shape(1) = len
      call c_f_pointer(c_string, string, string_shape)
    end function flu_tolstring
    

    where lua_tolstring has the following interface:

    function lua_tolstring(L, index, len) bind(c, name="lua_tolstring")
      use, intrinsic :: iso_c_binding
      type(c_ptr), value :: L
      integer(kind=c_int), value :: index
      integer(kind=c_size_t) :: len
      type(c_ptr) :: lua_tolstring
    end function lua_tolstring
    

    Finally, here is an attempt to clarify how a c_ptr can be interpreted as a Fortran character string: Assume you got a c_ptr pointing to the string:

    type(c_ptr) :: a_c_string
    

    And the length of it is given by a len variable with the following type:

    integer(kind=c_size_t) :: stringlen
    

    You want to get this string in a pointer to a character string in Fortran:

    character,pointer,dimension(:) :: string
    

    So you do the mapping:

    call c_f_pointer(a_c_string, string, [ stringlen ])
    

提交回复
热议问题