Reputation: 36838
I have the following C code:
typedef void (*mycallback) (char buf[128]);
void take_callback(mycallback cb)
{
}
I've written the equivalent Ruby FFI declarations as below (following advice for structs on FFI wiki):
callback :mycallback, [[:char, 128]], :void
attach_function :take_callback, [:mycallback], :void
When I run it, I get the following error:
`find_type': unable to resolve type '[:char, 128]' (TypeError)
It seems I'm not declaring the char array in the callback correctly. From the way arrays work in function arguments in C, I think I should use :pointer
instead of [:char, 128]
. But I'm not sure about the peculiarities of FFI. What's really the correct syntax here?
Upvotes: 2
Views: 1126
Reputation: 72514
Using :pointer
is the correct way for this kind of parameter, not only for callbacks, but in general. For example this isn't possible, too: typedef [:char, 128], :buffer_128
. The only exception are FFI::Struct
layouts where these types are allowed.
This is a little bit frustrating, especially since FFI so seamlessly supports null termined strings (via :string
). Fortunately it is still relatively ease to work with :pointer
arguments, too.
I am using this helper in some of my projects:
def with_buffer(size, clear: true, &)
FFI::MemoryPointer.new(:char, size, clear) do |buffer|
yield buffer
return buffer.read_string
end
end
Usage example: (from a CEC Ruby library I'm working on, stripped for clarity)
name = with_buffer(13) { |buffer| libcec_get_device_osd_name(*, buffer) }
Or when the called function wants to know the buffer size and you don't want to repeat that information:
cec_version =
with_buffer(50) do |buffer|
libcec_cec_version_to_string(*, buffer, buffer.size) # buffer.size == 50
end
Perhaps this is useful to someone...
Upvotes: 0
Reputation:
Arrays aren't passed by value in C -- they're passed as pointers to the first element, so :pointer
(or whatever is ordinarily used for char *
) should be correct.
Upvotes: 2