Declaring char array in Ruby FFI

1k views Asked by At

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?

2

There are 2 answers

1
AudioBubble On BEST ANSWER

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.

0
Daniel Rikowski On

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...