5

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?

francescalus
  • 30,576
  • 16
  • 61
  • 96
GPMueller
  • 2,881
  • 2
  • 27
  • 36

1 Answers1

6

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.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Possibly, is it also valid to use a fixed-length character string (within a block) like... `l_str = c_strlen(c_pointer); block; character(len=l_str), pointer :: f_ptr; call c_f_pointer(c_pointer, f_ptr); f_string = f_ptr; end block`? – roygvib Aug 13 '21 at 18:06
  • 1
    Yes, @roygvib, that would be possible. – francescalus Aug 13 '21 at 18:41
  • thanks! (I'm also preparing similar helper routines so looking at available codes) – roygvib Aug 13 '21 at 21:39
  • 1
    @roygvib, I'm not sure I like that approach, but I wouldn't be able to tell you why. I'd let it through review, but I'd probably be more demanding of comments than I would with some alternatives. Perhaps the reason it jars a touch is that, if you have a C array of characters it's more natural for the intermediate Fortran object also to be an array of characters rather than a storage associated scalar (both then being thrown away). – francescalus Aug 13 '21 at 21:55
  • 1
    @roygvib, or maybe it's that you can't do `block; character(len=l_str), pointer :: p1, p2; call c_f_pointer(cptr, p1); call c_f_pointer(cptr, p2); end block` (because you can't have two such Fortran character pointers pointing to the same C target). – francescalus Aug 13 '21 at 21:56
  • 1
    Yes, that must be why I don't like it. You get used to this approach, then one day you find yourself using it in a similar way where you aren't actually allowed to and your spaceship blows up. – francescalus Aug 13 '21 at 22:12
  • thanks very much for additional comments! Actually I also have a bit "uneasy" feeling about the code (in my comment above), though it seems to work with gfortran (for example). My another concern is that I might hit some varying behaviors depending on compilers (incl. issues), so I think I will go with the usual approach of first creating a temporary character array, then copying to a deferred-length string (after allocating with the right length). thanks much :) – roygvib Aug 14 '21 at 13:22