Module: FFI::DRY::StructHelper
- Defined in:
- lib/ffi/dry.rb
Overview
A module to add syntactic sugar and some nice automatic getter/setter methods to FFI::Struct, FFI::ManagedStruct, etc.
For example:
require 'rubygems'
require 'ffi'
require 'ffi/dry'
require 'pp'
class SomeStruct < FFI::Struct
include FFI::DRY::StructHelper
# we get a new way of specifying layouts with a 'dsl'-like syntax
dsl_layout do
field :field1, :uint16, :desc => 'this is field 1'
field :field2, :uint16, :desc => 'this is field 2'
end
end
ss0=SomeStruct.new
pp ss0. # we can look at definition metadata
# produces...
# [{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
# {:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
# And we have additional ways of instantiating and declaring values
# during initialization. (The FFI standard ways still work too)
raw_data = "\x00\x00\xff\xff"
ss1=SomeStruct.new :raw => raw_data
ss2=SomeStruct.new :raw => raw_data, :field1 => 1, :field2 => 2
ss3=SomeStruct.new {|x| x.field1=1 }
ss4=SomeStruct.new(:raw => raw_data) {|x| x.field1=1 }
[ ss0,
ss1,
ss2,
ss3,
ss4].each_with_index {|x,i| pp ["ss#{i}",[x.field1, x.field2]]}
# produces...
# ["ss0", [0, 0]]
# ["ss1", [0, 65535]]
# ["ss2", [1, 2]]
# ["ss3", [1, 0]]
# ["ss4", [1, 65535]]
Defined Under Namespace
Modules: ClassMethods
Instance Attribute Summary collapse
-
#dsl_metadata ⇒ Object
readonly
Returns the value of attribute dsl_metadata.
Class Method Summary collapse
Instance Method Summary collapse
-
#copy(grown = 0) ⇒ Object
Returns a new instance of self.class containing a seperately allocated copy of all our data.
-
#copy_size ⇒ Object
This method is called when creating a copy of self.
-
#initialize(*args) {|_self| ... } ⇒ Object
Allows setting structure fields on initialization to ::FFI::Struct.new as well as a “yield(self) if block_given?” at the end.
-
#ptr_to(field) ⇒ Object
Returns a pointer to the specified field, which is the name assigned to a member in the layout.
-
#set_fields(params = nil) ⇒ Object
Sets field values in the struct specified by their symbolic name from a hash of ‘:field => value’ pairs.
Instance Attribute Details
#dsl_metadata ⇒ Object (readonly)
Returns the value of attribute dsl_metadata.
77 78 79 |
# File 'lib/ffi/dry.rb', line 77 def @dsl_metadata end |
Class Method Details
.included(base) ⇒ Object
225 226 227 |
# File 'lib/ffi/dry.rb', line 225 def self.included(base) base.extend(ClassMethods) end |
Instance Method Details
#copy(grown = 0) ⇒ Object
Returns a new instance of self.class containing a seperately allocated copy of all our data. This abstract method should usually be called with super() from overridden ‘copy’ implementations for structures containing pointers to other memory or variable length data at the end.
Note also that, by default, this implementation determine’s size automatically based on the structure size. This is comparable to sizeof(some_struct) in C. However, you can supply a ‘grown’ parameter which can be used to add to the size of the copied instance as it is allocated and copied.
151 152 153 |
# File 'lib/ffi/dry.rb', line 151 def copy(grown=0) self.class.new( :raw => self.to_ptr.read_string(self.copy_size+grown) ) end |
#copy_size ⇒ Object
This method is called when creating a copy of self. It can be overridden by derived structures to return another size. This is sometimes done to account for alignment issues, etc.
158 159 160 |
# File 'lib/ffi/dry.rb', line 158 def copy_size self.size end |
#initialize(*args) {|_self| ... } ⇒ Object
Allows setting structure fields on initialization to ::FFI::Struct.new as well as a “yield(self) if block_given?” at the end.
Field initialization happens if there is only one argument and it is a Hash.
The params hash is taken as a set of values for fields where the hash keys specify the field names to set.
Note: The :raw parameter is a special tag in the hash. The value is taken as a string and initialized into a new FFI::MemoryPointer which this Struct then overlays.
If your struct layout has a field named :raw field, it won’t be assignable through the hash argument.
See also: set_fields() which is called automatically on the hash, minus the :raw tag.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/ffi/dry.rb', line 100 def initialize(*args) @dsl_metadata = self.class. params=nil if args.size == 1 and (oparams=args[0]).is_a? Hash params = oparams.dup if raw=params.delete(:raw) super( ::FFI::MemoryPointer.new(raw.size).write_string(raw) ) else super() end else super(*args) end set_fields(params) yield self if block_given? end |
#ptr_to(field) ⇒ Object
Returns a pointer to the specified field, which is the name assigned to a member in the layout.
164 165 166 167 |
# File 'lib/ffi/dry.rb', line 164 def ptr_to(field) x = self[field] # this is actually a test, to raise if missing return (self.to_ptr + self.offset_of(field)) end |
#set_fields(params = nil) ⇒ Object
Sets field values in the struct specified by their symbolic name from a hash of ‘:field => value’ pairs. Uses accessor field wrapper methods instead of a direct reference to the field (as in “obj.field1 = x”, not “obj = x”). The difference is subtle, but this allows you to take advantage of any wrapper methods you override when initializing a new object. The only caveat is that the wrapper method must be named the same as the field, and the field must be included in members() from the layout.
This method is called automatically if you are using the initialize() method provided in the Struct class and passing it a Hash as its only argument.
131 132 133 134 135 136 137 138 139 |
# File 'lib/ffi/dry.rb', line 131 def set_fields(params=nil) (params || {}).keys.each do |p| if members().include?(p) self.__send__(:"#{p}=", params[p]) else raise(::ArgumentError, "#{self.class} does not have a '#{p}' field") end end end |