Thanks to Valentin for meeting with me offline. We arrived at a final solution to this problem:
Declare a callback this way:
struct Message
error_code::Cint
return_code::Cint
session_present::Cuchar
end
callback = ForeignCallbacks.ForeignCallback{Message}() do msg
# actual callback impl in here
return nothing
end
function on_connection_complete(connection::Ptr{aws_mqtt_client_connection}, error_code::Cint, return_code::Cint, session_present::Cuchar, userdata::Ptr{Cvoid})
# convert and then load the pointer because this function must be type-stable
token = Base.unsafe_load(Base.unsafe_convert(Ptr{ForeignCallbacks.ForeignToken}, userdata))
ForeignCallbacks.notify!(token, Message(error_code, return_code, session_present))
return nothing
end
Store the user_data
this way:
token = Ref(ForeignCallbacks.ForeignToken(callback))
Base.unsafe_convert(Ptr{Cvoid}, token) # this is the user_data
Then you can create the callback C function:
on_connection_complete_cb = @cfunction(on_connection_complete, Cvoid, (Ptr{aws_mqtt_client_connection},Cint,Cint,Cuchar,Ptr{Cvoid}))
Relevant variables like token
and on_connection_complete_cb
must be GC.@preserve
-ed while in use.