How to load the values returned by callback functions (defined in a Julia wrapper) to C functions

[4/6 and 5/6 solved it] I am trying to wrap ngspice_jll.jl And have generated wrapper functions to sharedspice.h file.

A typical function has pointers to callback functions as arguments. Initialization looks like:

function ngSpice_Init(sendchar, statfcn) #and 5 more similar callbacks
    ccall((:ngSpice_Init, libngspice), Cint, (Ptr{Cvoid}, Ptr{Cvoid}), sendchar, statfcn)
end

where sendchar = @cfunction($SendChar, Cint, (Ptr{Char}, Cint, Ptr{Cvoid})).ptr

(And SendChar is supposed to print the string pointed by Ptr{Char})

After executing ngSpice_Init(...), and loading circuit, I want to access the sendchar and its arguement. (This and other callbacks are expected to contain info about the simulation being run) When I unsafe_load it, as expected, points to nothing. unsafe_pointer_to_objref crashes the repl. What should I do to access the string (and other values) returned by the callback function.

sendchar looks like a callback. Why are you trying to unsafe_load it?

The ngSpice functions return 0 on execution. Most of the time it prints the status to its own console. And callbacks are supposed to have this info.

Alright, so @cfunction creates a C pointer that is a callback to a julia function (in this case SendChar). So whenever ngspice invokes the sendchar function pointer you passed, it will end up coming through on the Julia side by invoking the SendChar function. Doing an unsafe_pointer_to_objref on sendchar doesn’t make sense, because it’s not a julia object. It’s a c function pointer. What you’ll want to do is implement the SendChar method and inside there handle whatever arguments ngspice gives to the callback.

1 Like

This post is probably obsoleted but it’s still a good reading material on how to pass Julia callback functions to C.

I didn’t test the following code, but I guess you could get the idea.

function sendchar(str::Ptr{Cchar}, id::Cint, userData)::Cint
    push!(userData, unsafe_string(str))
    # I'm not sure what should be returned by this callback function, the header file doesn't tell much
    # maybe the id, maybe just 0 as you mentioned above, you may need to dive into the code of `ngSpice_Init`.
    return 0 or id? 
end

user_data = String[]  # you may need a more complex Julia structure for user_data, here I just make it as a string vector.
sendchar_fptr = @cfunction(sendchar, Cint, (Ptr{Cchar}, Cint, Ref{Vector{String}}))

function ngSpice_Init(sendchar, userData) #and 5 more similar callbacks
    ccall((:ngSpice_Init, libngspice), Cint, (Ptr{Cvoid}, Any), sendchar, userData)
end

ngSpice_Init(sendchar_fptr, user_data)

1 Like

Thank you @Keno and @Gnimuc.

Although I had defined function sendchar(...), I had totally mistaken that it would work only after I explicitly do “something” with the sendchar_ptr before invoking it. Now the workflow is clear. Thanks again!