Designing a C ABI layer for a multi-lingual project

Hello all,

I’m interested in creating a multi-lingual project with a Julia backend. I’d like to do this through a C ABI layer and would like advice on how to structure it.

Since my usecase is a bit difficult to explain, I’ve likened it to a basic video game. Here’s my current idea of how to structure it:

Julia has a world struct and some way to update the world:

module Game

mutable struct World
    playerx :: Float32
    playery :: Float32
    playerhealth :: Int
end

function step!(w::World, ...)
  ...
end

end #module

It has a function callable from C that creates a World and returns a pointer:

module CLayer

const _registry = Dict{UInt, Base.RefValue{Model}}()

Base.@ccallable function create_world(...) :: Ptr{Nothing}
    w = Game.World(...)
    r = Ref(w)
    p = UInt(pointer_from_objref(r))
    _registry[p] = r  
    return Ptr{Nothing}(p)
end

end #module

Module CLayer also has a function callable from C that updates the world:

Base.@ccallable function step(ptr::Ptr{Nothing}, ...) :: Cvoid
    w = _ref(ptr)
    Game.step!(w[], ...)

    unsafe_copyo!(ptr, pointer(encode(w), length(w))

    nothing
end

I could then compile that with

using PackageCompiler

create_library(
        \"$IN_DIR\",
        \"$OUT_DIR\";
        lib_name        = \"game\",
        incremental     = false,
        filter_stdlibs  = false,
        force           = true,
)

Then, I would have a header file that looked something like this:

#include <stdint.h>

void* create_world(...);
void step(void* world, ...);

I think this should allow for other languages to then implement these C methods. I’ve heard of a similar approach being used for plugins, but I think that would require Julia code also calling C code.

I was wondering what others thought of this approach. Do you think it will scale? Is it performant? Is there a bug somewhere?

Please excuse the many beginner mistakes I’m sure I have made in this post – this is definitely not my forte.