简体   繁体   中英

How to correctly read a C-string into a Fortran string of unspecified length?

The following function is supposed to convert a C string into a Fortran string and works fine in Release builds, but not in Debug:

! Helper function to generate a Fortran string from a C char pointer
function get_string(c_pointer) result(f_string)
    use, intrinsic :: iso_c_binding
    implicit none
    type(c_ptr), intent(in)         :: c_pointer
    character(len=:), allocatable   :: f_string

    integer(c_size_t)               :: l_str
    character(len=:), pointer       :: f_ptr

    interface
        function c_strlen(str_ptr) bind ( C, name = "strlen" ) result(len)
        use, intrinsic :: iso_c_binding
            type(c_ptr), value      :: str_ptr
            integer(kind=c_size_t)  :: len
        end function c_strlen
    end interface

    l_str = c_strlen(c_pointer)
    call c_f_pointer(c_pointer, f_ptr)

    f_string = f_ptr(1:l_str)
end function get_string

However, it seems that c_f_pointer does not tell the Fortran pointer-to-string, f_ptr , the length of the string it is pointing to. In Debug builds, where bounds-checking is active, this results in

Fortran runtime error: Substring out of bounds: upper bound (35) of 'f_ptr' exceeds string length (0)

I'm using gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 and set the standard to 2008.

My question: Is there any way to tell the f_ptr its length without changing the declaration or am I doing something fundamentally the wrong way here?


It seems to run correctly if I specify the shape, but for that f_ptr needs to be an array:

character(len=:), allocatable   :: f_string
character(len=1), dimension(:), pointer :: f_ptr
...
call c_f_pointer(c_pointer, f_ptr, [l_str])

However, I cannot find a way to transform that string of rank 1 to the character(len=:), allocatable:: f_string , which apparently has rank 0.

My second question: Is there any way to transfer the f_ptr data into the f_string in this example?

You cannot use c_f_pointer to set the length of the Fortran character pointer (F2018, 18.2.3.3):

FPTR shall be a pointer, shall not have a deferred type parameter [...]

A deferred-length character scalar (or array) therefore cannot be used (the length is a type parameter).

You can indeed use a deferred- shape character array as fptr and then use any number of techniques to copy the elements of that array to the scalar (which is rank-0 as noted).

For example, with substring assignment (after explicitly allocating the deferred-length scalar):

allocate (character(l_str) :: f_string)
do i=1,l_str
  f_string(i:i)=fptr(i)
end

Or consider whether you can simply use the character array instead of making a copy to a scalar.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM