An example
This FFI::Struct
(meant to mirror a C struct), has a single value, named “value”, of type “double”
class SimpleStruct < FFI::Struct
layout :value, :double
end
a = SimpleStruct.new # does a malloc on one of those structs
a[:value] = 32 # sets its internal value to 32
Passing structs as value or pointer parameters to functions
Assuming you have a C library that looks like this, where the function takes a struct as an argument by value:
typedef struct _WHAT {
int d;
} WHAT;
int doit_by_value(WHAT w) {
printf("%d\n", w.d);
return w.d;
}
int doit_by_ptr(WHAT *w) {
printf("%d\n", w->d);
return w->d;
}
The resulting FFI wrapper code for the function should look like this:
class WHAT < FFI::Struct
layout :d, :int
end
attach_function 'doit_by_value', [ WHAT.by_value ], :int
attach_function 'doit_by_ptr', [ WHAT.by_ref ], :int
w = WHAT.new
w[:d] = 123
Foo.doit_by_value(w)
Foo.doit_by_ptr(w)
StackOverflow Question Relating to this
When allocated
When you call FFI::Struct.new
, it allocates an “internal bytes” worth of memory right then. When you then do a set, like struct[:member] = 3
, the bits within that struct are set right then. The inverse is also true; x = struct[:member]
does a read from the raw memory (and translation into a ruby object), each time you access it. Memory is “zeroed out” when it is first allocated, unless you pass in your own Pointer or specify it to not clear memory (additional notes). If you pass it a Pointer it basically uses that as its base instead of allocating anything.
# Pass false as a 3rd argument to skip memory initialization
pointer = FFI::MemoryPointer.new :char, SimpleStruct.size, false
a = SimpleStruct.new pointer
Note that you can get the actual address of the struct by using the #pointer
method.
Casting
The same technique can be used to cast a “formless” blob of memory to a struct.
class ComplexStruct < FFI::Struct
layout :context, :pointer,
:value1, :int32,
:value2, :uint64,
:value3, :char,
:next, :pointer
end
def cast_to_complex_struct pointer
ComplexStruct.new pointer
end
Calling cast_to_complex_struct
and passing it a blob of memory will return a struct object mapped to that memory location. Code can then directly access the struct fields and operate on them.
my_struct = cast_to_complex_struct(FFI::MemoryPointer.new :char, ComplexStruct.size)
my_struct[:value1] = rand(1000)
my_struct[:value2] = my_struct[:value1] * my_struct[:value3]
Nested Structs
It’s possible to nest structs within one another by direct reference. Simply use the name of the struct type you wish to nest within another. If instead you need a pointer to a struct of that type, use the next section, “Nested pointers” for more information.
class InnerStruct < FFI::Struct
layout :, :uint32,
:pMessage, :string
end
class OuterStruct < FFI::Struct
layout :value, :uint32,
:inner, InnerStruct # this sets up our inner struct!
end
Nested pointers
You can define nested pointers to structs within structs using FFI::Struct.ptr
method. It’s important to keep in mind, that each object stored in a pointer reference needs to stored in a ruby variable, so that it isn’t garbage collected until the struct is no longer accessed. See Core Concepts for more description about memory management.
class Struct2 < FFI::Struct
layout :age, :int
end
class Struct1 < FFI::Struct
layout :id, :int,
:data, Struct2.ptr
end
struct = Struct1.new
struct2 = Struct2.new
struct[:data] = struct2
struct[:data][:age] = 27
Don’t reference the inner Struct2 object by a pointer only! The ruby garbage collector doesn’t know about it and could free the memory behind Struct2 before the data has been processed. So this is dangerous: ```ruby struct = Struct1.new struct[:data] = Struct2.new # Struct2.new object could be GC’ed immediately! struct[:data][:age] = 27
Pointers to Functions
Some libraries have factory functions that return structures containing pointers to other functions in the library. Instead of modeling these pointers using the normal :pointer
type, you can simply create an anonymous or named callback type using the callback
method.
class FunctionTable < FFI::Struct
layout :function1, callback([:pointer, :int], :void) # A pointer to an anonymous function that takes a pointer and an integer
# or more verbose with a named callback:
layout :function1, :completion_function # A pointer to the named callback function
end
callback :completion_function, [:pointer, :long, :uint8], :void # define a named callback
attach_function :factory, [], :FunctionTable.by_ref
Then, once the factory function has filled in the pointers, you can call them using the regular call()
method.
vtable = factory() # Creates and returns a new structure
# Call the function that the library provided
vtable[:function1].call(my_pointer, my_integer)
Char arrays
from C
struct {
uint8 Value;
uint8 String[SIZE_OF_ARRAY];
} MyArray_t;
to Ruby
class MyArray_t < FFI::Struct
layout :Value, :uint8,
:String, [:uint8, SIZE_OF_ARRAY]
end
# use via:
my_array_struct[:String].to_ptr.read_string
Set them like
a = MyArray_t.new
a[:String][0] = 33 # set a single byte within the struct
or
a[:String].to_ptr.put_string(0, "foo")
custom packed structs
By default, ffi will assume the structs you mentioned are to be packed “like normal for your system” — if this is not the case, as in if you use #pragma pack
in msvc, or __attribute__((__packed__))
on gcc, then you’ll need to specify the struct packing boundary.
class CustomPacked < FFI::Struct
pack 1 # pack all members on a 1 byte boundary
layout :a, :char,
:b, :int, # starts at offset=1, not offset=4
end
Array of Structs
You can access an array of structs by stepping through the array in increments of the struct size, and casting each blob of memory using FFI::Pointer#.
Here is an example of a parent struct that contains an array of structs, and each member of the array also contains a union.
From c:
// parent struct contains an array of child structs in *val
typedef struct {
uint len;
F_TextItemT *val;
} F_TextItemsT;
typedef struct {
int offset;
int dataType; // indicates what part of the union is valid
union {
char *sdata; // string data
int idata; // int data
} u;
} F_TextItemT;
In Ruby the structs and union look like this:
class FTextItemU < FFI::Union
layout :sdata, :string,
:idata, :int
end
class FTextItemT < FFI::Struct
layout :offset, :int,
:dataType, :int,
:u, FTextItemU
end
class FTextItemsT < FFI::Struct
layout :len, :uint,
:val, :pointer
end
This code accesses members of the array using FFI::Pointer# by stepping through the array at increments of the struct size:
tis = FM.FApiGetText(docid, flowid, (FM.FTIString | FM.FTILineEnd));
# Traverse text items and print strings and line ends.
0.upto(tis[:len]-1) do |i|
s = FM::FTextItemT.new(tis[:val] + (i * FM::FTextItemT.size))
if s[:dataType] == FM.FTIString
puts s[:u][:sdata]
end
end
Alternatively, you can use Pointer.new() to create a new pointer with a different type_size (the size used to step along it using the [] method).
e.g.
val_array = FFI::Pointer.new(FM::FTextItemT, tis[:val])
0.upto(tis[:len]) do |i|
s = FM::FTextItemT.new(val_array[i])
# do stuff with s struct here
end
If you need to pass an array of structs to a function, you can use the same approach: first pre-allocate some memory for the array, and then step through it. If you wish, you can create an array of Ruby objects, each of which points to the correct place in the underlying storage. The following example uses struct iovec as used by sendmsg(2) and recvmsg(2)
class IOVec < FFI::Struct
layout :base, :pointer,
:len, :size_t
end
iovlen = 3
iov = FFI::MemoryPointer.new(IOVec, iovlen)
iovs = iovlen.times.collect do |i|
IOVec.new(iov + i * IOVec.size)
end
iovs[0][:base] = #...
iovs[0][:len] = #...
iovs[1][:base] = #...
iovs[1][:len] = #...
iovs[2][:base] = #...
iovs[2][:len] = #...
msghdr.msg_iov = iov
msghdr.msg_iovlen = iovlen
Simpler struct arrays
An easier way to handle arrays of structs is to use a single memory pointer.
class SomeStruct < FFI::Struct
# . . .
end
# where length is the size of the array
ary = FFI::MemoryPointer.new( :uchar, SomeStruct.size() * length )
This can be passed directly into a C-function and will have the requisite memory allocated to it to contain all instances of the struct you’re using.
To read out of it again, simply do the following:
# determine the length of the data
bytes = SomeStruct.size()
# read a struct from a single index; this requires two steps:
# 1. first we declare the container pointer.
# 2. then we cast it.
data = ary.slice( bytes * index, bytes )
instance_of_somestruct = SomeStruct.new( data )
If you would rather read the entirety of the pointer as a ruby Array, you can instead do this:
# determine the length of the data
bytes = SomeStruct.size()
# create an array
instances_of_somestruct = Array.new( ary.size / bytes ){|index|
# set this index to the new SomeStruct instance
SomeStruct.new( ary.slice( index * bytes, bytes ) )
}
Getting Pointers to Struct Fields
Some functions need the address of a field to be passed as an argument. There is no built-in method for returning a field’s address. Luckily we can mimic this behavior by using a {FFI::Pointer} and a field offset.
struct in_addr {
uint32_t s_addr; // that's a 32-bit int (4 bytes)
};
struct sockaddr_in {
short int sin_family; // Address family, AF_INET
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Same size as struct sockaddr
};
char* result;
char ip_string[INET_ADDRSTRLEN];
struct sockaddr_in sa; // pretend this is loaded with something
result = inet_ntop(AF_INET, &(sa.sin_addr), ip_string, INET_ADDRSTRLEN);
<pre class="code"><code class="">
Translating this to Ruby is fairly straightforward.
<pre class="code ruby"><code class="ruby"> class SockAddrInStruct < FFI::Struct
layout :sin_len, :uint8,
:sin_family, :sa_family_t,
:sin_port, :ushort,
:sin_addr, :uint32,
:sin_zero, [:uint8, 8]
end
attach_function :inet_ntop, [:int, :pointer, :pointer, :int], :string, :blocking => true
ip_string = FFI::MemoryPointer.new(:uint8, INET_ADDRSTRLEN)
sa = SockAddrInStruct.new # pretend this is loaded with something
field_ptr = FFI::Pointer.new(:uint8, sa.pointer.address + sa.offset_of(:sin_addr))
result = inet_ntop(AF_INET, field_ptr, ip_string, INET_ADDRSTRLEN);
Gotchas
multidimentional arrays aren’t yet fully supported
any type of class descendancy/hierarchy is not supported, at least it appears to not be.
Helpers
Currently structs only allow access “like a Hash” via instance[:member]
. If you desire methods for access, you can use ffi swig, or nice-ffi or roll your own
Helper example via “method_missing” builtin
You can make a simple helper method to access struct members as if they were created with `attr_accessor` with the following code, which exploits the `method_missing()` builtin method for most Ruby objects:
class FFI::Struct
# Use the symbol passed from the method for accessing the analogous field.
# This method can also take a &block, but we would not use it.
def method_missing( sym, *args )
# convert symbol to a string to allow regex checks
str = sym.to_s
# derive the member's symbolic name
member = str.match( /^([a-z0-9_]+)/i )[1].to_sym
# raise an exception via the default behavior if that symbol isn't a member!
super unless members.include? member
# this ternary checks for the presence of an equals sign (=) to indicate an
# assignment operation and changes which method we invoke and whether or not
# to send the splatted arguments as well.
(str =~ /=$/) ? send( :[]=, member, *args ) : send( :[], member )
end
end
Platform dependent offsets of struct members
Offsets of struct members are calculated based on the value types. There is a deprecated way to define offsets explicit by using [[Automatic Struct Layout]].