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.