How can I pass a string to Fortran? The following methods doesn’t seem to work
julia> T = Ref{Char}
Ref{Char}
julia> Base.unsafe_convert(T, Base.cconvert(T, "foo"))
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Char
Here, If I choose T to be Ref{Char}, Ref{String}, Ref{Cstring}, unsafe_convert errors. If instead I choose T to be Cstring or String, the Fortran side seems to get an empty string.
Minimum example:
module julia_interface
implicit none
public :: print_name
private
CONTAINS
subroutine print_name(name)
character(len=*), intent(in) :: name
print *, name
end subroutine print_name
end module julia_interface
No. Char is 4 bytes but uses a Julia-specific encoding (that maps directly to UTF8). Fortran characters are 1-byte by default in most compilers, and were intended originally for ASCII but maybe could be used for UTF-8 encoded Unicode if the Fortran code doesn’t look at them too closely. Some Fortran compilers also support UTF32 or UTF16 (sometimes archaically called ucs4 and ucs2) via different character “kinds”, but only if the Fortran code declares it explicitly.
But never pass Char data directly. If the Fortran code is expecting UTF32, you can get it via [UInt32(c) for c in str]
Honestly, I still don’t understand exactly what is happening under the hook. I learned a bit about Fortran during the past two days, with the sole purpose of wrapping a subroutine in Julia. I assume Julia will automatically add a \0 to foo so the actual length is 4, which is not needed on the Fortran side?
Actually, Julia strings internally already have a zero byte at the end of their data, to make it easier to pass them to C libraries without copying the data.
When you declare the argument as Cstring, however, it checks that the string contains no 0 bytes (and throws an error if there are), since this would lead to a corrupted string in a C routine expecting NUL-terminated data. You don’t want that here because Fortran doesn’t treat 0 as the end of the string, and you are passing the length explicitly. So you should declare the argument as Ptr{UInt8} instead.
You should declare this as subroutine print_name(name, length) bind(C). That way you should be able to ccall it as :print_name without compiler-specific name-mangling (:julia_interface_mp_print_name_ is not portable). See e.g. the gfortran docs on interoperating with C.
I would declare this as integer(C_INT) rather than integer, in which case you can use Cint (rather than Int32) as the argument type in Julia and be sure that will work, even if the Fortran code is compiled in ILP64 mode.
Better yet, declare it as integer(C_INTPTR_T), in which case you can simply pass the size as Int (the corresponding type in Julia) and it will work on a 64-bit machine even in the unlikely event that your string is more than 2GiB in length.
You can also use the VALUE attribute for the variable, in which case you don’t need Ref in the ccall (the value is passed directly, not as a pointer).
Just want to mention I have to change name to assumed-size array because otherwise the compiler complains “A character dummy argument with length other than 1 is not interoperable”.