I'm trying to get my head around FFI in ruby. Is there no way to make use of the return from an FFI callback?
Here's my minimal example:
require 'ffi'
class Foo
extend FFI::Library
ffi_lib File.expand_path('fun.o')
callback :incoming_rpc, [:string], :string
attach_function :do_some_work, [:incoming_rpc, :string], :string, blocking: true
def initialize
@callback = build_callback_runner
output = do_some_work(@callback, "Ruby init...")
puts "Output: #{output.inspect}"
end
def build_callback_runner
FFI::Function.new(:string, [:string]) do |name|
puts "Inside runner: #{name}"
"DO YOU READ ME?"
end
end
end
Foo.new
Here is the C function I'm calling: yep, I know it's not brilliant C, my original receiver was in go, which also fails in exactly the same way. (I won't promise I'm writing brilliant go, either)
// Name: fun.c
// Compiled with: gcc -shared -o fun.o fun.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char* (*callbkfn)(char*);
extern char* do_some_work(callbkfn fn, char* name);
char* do_some_work(callbkfn fn, char* userdata) {
printf("do_some_work param: %s\n", userdata);
printf("callback output: %s\n", fn(strdup("Hello from C")));
return strdup("Returned from C");
}
Output:
do_some_work param: Ruby init...
Inside runner: Hello from C
callback output: (null)
Output: "Returned from C"
It's that null in the callback output I can't seem to shake. How do you pass the "return" value of a callback FFI::Function or callback proc back into C? The FFI documentation always seems to set callbacks as :void, and I'm guessing the answer is somewhere on the pointers page - but I'm drawing a blank (much like my callback)
char *was allowed as a return value. That’s why e.g. all shell utilities use return codes, not return anything. - Aleksei Matiushkinchar *, and while that seems to work I don’t know the details of Ruby or FFI enough to know if that would be safe with GC etc. - matt