1 (edited by Lewist57 2023-09-20 22:31:09)

Topic: Passing strings from Fortran to other languages

Regarding the interoperability of Fortran with C and other languages, it seems that even Fortran 2018 has not worked out all the bugs regarding sending a string from one language to a Fortran DLL, and then sending it from Fortran back to the calling language.

I have not put a lot of thought into it and have not tried this (yet), but why not simply convert the characters in a string into their ASCII (or other base) equivalents, and then send between C/C#/VB.NET/et al  and Fortran as a 1D integer array? 

For example, instead of trying to send "This is a string" between the two languages, why not send

(84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 116, 114, 105, 110, 103)

back and forth (of course using the appropriate routines in C (or other language) and Fortran to do the character to integer conversions, parsing of the string into ASCII, connotation of characters, etc.)? I am sure I am not the first one to think of this, but it seems a lot easier to embed the code to do the ASCII conversions in the two programs than to jump through hoops via iso C binding for strings.

Thoughts?  Could it be that simple?

Re: Passing strings from Fortran to other languages

When I'm passing strings to C from Fortran, I usually try to be careful.  This task is difficult because C doesn't formally have a definition of a string, just an array (actually just a pointer to a memory block) that happens to end in a null character (and only if we're talking about ASCII...).  Fortran, on the other hand, knows a lot more about arrays and string lengths, most notably their dimensions.

When I'm feeling like shooting from the hip, a string from Fortran can be passed pretty easily if you include that null character, something like:

    interface
       function strlen(str) bind(c)
       use iso_c_binding
       character(len=*)::str
       integer(kind=c_int)::strlen
       end function strlen
    end interface

    ...
    
    i = strlen(trim(fortran_string)//c_null_char)

So the above should "work" in most cases, but there are problems.  What you're talking about is passing the ASCII values.  I agree, that's the most robust way.  But none of the values will exceed 255, so we might as well use CHARACTER instead of integer.  The most correct way, in my opinion, is using an array of CHARACTERs with a KIND of C_CHAR and a TARGET attribute for passing so we can pass a true C pointer.  It's wordy, but it makes me feel better:

    use iso_c_binding

    character(kind=c_char), dimension(40)::c_passing
    integer::i

    interface
       function strlen(str) bind(c)
       use iso_c_binding
       type(c_ptr), value::str
       integer(kind=c_int)::strlen
       end function strlen
    end interface

    ...
    ! Fill the array completely with zeroes
    c_passing = c_null_char

    ! Fill the array with the string components, but make sure we have a zero on the end
    do i = 1, min(len_trim(fortran_string), size(c_passing)-1 )
        c_passing(i) = fortran_string(i:i)   ! <- to transfer, character by character, into the C passing array
    end do

    i = strlen(c_loc(str))

The above code treats C strings, in Fortran, exactly the same way C would treat them, so I guess it's technically more correct than the earlier example.  But we're basically doing exactly what you proposed.

Jeff Armstrong
Approximatrix, LLC