Fortran-Julia interface for program containing non-c-interoperable derived type

I’m still new to Julia (and this discourse), but couldn’t find the answer to this question anywhere.

I have a large Fortran legacy code base which I’m trying to interface with a high-level language. I got something kind of working with Cython, and a MWE for that can be found on StackOverflow here: Fortran C interface for program containing non-c-interoperable derived type.

I’ve read/heard that interface C/Fortran to Julia is really simple, so I want to do the same sort of thing in Julia, I guess with ccall, though I’m getting quite lost.

Here is some Fortran code,

module somemodule
    use, intrinsic :: iso_c_binding
    implicit none

    type :: sometype
       integer(c_int), allocatable :: a(:)
       integer(c_int) :: b
    end type

    type, bind(c) :: someothertype
       integer(c_int) :: c
    end type someothertype

 contains

    ! example routine that processes the content of the type
    subroutine sometype_x2_f(x)
       type(sometype), intent(inout) :: x

       x%a = 2 * x%a
    end subroutine

    subroutine sometype_init_f(x, n)
        type(sometype), intent(inout) :: x
        integer(c_int), intent(in) :: n
        integer(c_int) :: i
        x%a = [(i, i=0, n)]
    end subroutine sometype_init_f

    integer(c_int) function sometype_sum_f(x) result(res)
        type(sometype), intent(inout) :: x
        res = sum(x%a)
    end function sometype_sum_f

    subroutine someothertype_x2_f(x)
       type(someothertype), intent(inout) :: x

       x%c = 2 * x%c
    end subroutine

 end module

which I can compile into a shared .so library and then call into a duplicate of someothertype in Julia:

mutable struct SomeOtherType
    c::Cint
end
x = SomeOtherType(2)

println("x = $x (before)")
ccall((:__somemodule_MOD_someothertype_x2_f, "./libsometype_module.so"), Cvoid, (Ref{SomeOtherType},), x)
println("x = $x (after)")

which gives the expected

x = SomeOtherType(2) (before)
x = SomeOtherType(4) (after)

(hooray)

In my code, sometype%a might be very large, so I would not want it duplicated in Julia (at least until I manage to rewrite the Fortran into Julia ;)). is there a way to keep the x%a variable opaque to Julia (or otherwise avoid storing it twice, as I guess is done for someothertype%c, e.g. with pointers) while still getting access to x%b, and running (Fortran) functions that use/modify x%a, such as sometype_x2_f? Do I still need all the boilerplate code for wrapping these functions as I did in Cython (somemodule_wrap in the SO thread and getter/setters for other variables e.g. x%b)?

There is a Ptr{Nothing} Julia type that is analogous to a void* in C. You can use unsafe_load and unsafe_store! to read or write to the pointer. There is also unsafe_wrap which allows you to wrap a Julia Array around a pointer.

Thanks for the reply, do I understand correctly that these would basically require the same sort of solution as Cython? i.e. I would need to make setters and getters for all members (which I want to access) in the Fortran derived type and write C-interoperable wrappers? I guess it looks like boilerplate code then is essentially unavoidable here.

Thr Cython solution should work here as well.

I think we can do better by creating a struct with a compatible memory layout as your Fortan types.

What would the C equivalent of your Fortran types be?

Just to make sure, here is the Julia manual section on this:

https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/

Yes, it would be nice to do better than Cython, specifically if we can have a little less boilerplate code (though maybe this is not possible).

Because of the allocatable array, my sometype type has no C equivalent (in reality the type is a fair bit more complicated, but I’m using this as a stand-in). I’m happy not being able to access it directly (as would be done with C/Cython), but I’d like to be able to call Fortran functions involving it (which I guess is where the pointers come in) and manipulate the other members of the type (in my example, sometype%b).

I’ve seen the link but haven’t found a solution to my problem on there. My impression, though, is that I need to basically follow the solution in the StackOverflow thread (which involves a lot of wrapper functions in Fortran), then use ccall with pointers.

I think you are correct that there is no C equivalent to sometype. However, I think you can can return pointers to each of the elements of sometype. That is we can obtain pointers to a and b.

What I’m wondering is what about the large datatype that a points to? Can we map that to C and thus Julia?

Julia is basically has its Cython equivalent builtin. We can operate with C really well. Julia operates with Fortran via a C interface, so essentially have those same issues. However, Julia structs are designed to be C compatible.

My Fortran knowledge is limited. Perhaps @certik might be helpful here.

I also see an extended discussion on this matter on the Frotran forums:

By “the large datatype that a pointers to” do you mean what the actual datatype is? The actual data type is an array that’s shared across machines (i.e. it has a bunch of MPI information baked in). It’s also one of many generated with fypp. I’m sure with enough effort it’s possible to convert, but (1) I don’t really understand the structure and the author (no longer in the group) did a good job providing an understandable interface, and (2) these are very large: by my understanding by interoperating with Julia this way we are essentially copying over the entire data structure to Julia to operate further. This would be very expensive in terms of memory. I’d rather not open that can of worms and just never directly access it in Julia, if possible. I chose a to be allocatable in this MWE specifically because I know it’s not C-interoperable so it works as a proxy.

Is this the “getters and setters” I mentioned? Not a big deal, just annoying boilerplate code (maybe less so with metaprogramming tools). One would have to provide get_b and set_b Fortran functions, basically, or can you somehow avoid that? Maybe it is avoidable because Julia has pointers (unlike Python). Can you explain how you would access (say) b without redefining the structure in Julia?

Interesting tidbit: the person asking the question on the discourse link you sent appears to be the same one who answered my StackOverflow question. I’ll have to take a look; you are right that there seems to be some overlap with that question and mine. Thank you!