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.
59 60 61 |
# File 'lib/ffi/dry.rb', line 59 def @dsl_metadata end |
Class Method Details
.included(base) ⇒ Object
207 208 209 |
# File 'lib/ffi/dry.rb', line 207 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.
133 134 135 |
# File 'lib/ffi/dry.rb', line 133 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.
140 141 142 |
# File 'lib/ffi/dry.rb', line 140 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.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/ffi/dry.rb', line 82 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.from_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.
146 147 148 149 |
# File 'lib/ffi/dry.rb', line 146 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.
113 114 115 116 117 118 119 120 121 |
# File 'lib/ffi/dry.rb', line 113 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 |